GarriBlog is a microservice platform for people to share their thoughts.
This is a proof-of-concept, which demonstrates Microservice Architecture Pattern using Moleculer and Docker.
Moleculer is a fast, modern and powerful microservices framework for Node.js. It helps you to build efficient, reliable & scalable services. Moleculer provides many features for building and managing your microservices.
GarriBlog is decomposed into several core microservices. All of them are independently deployable applications, organized around certain business domains.
Contains general user management.
fields: ["_id", "username", "password", "bio", "image"]
Method | Path | Description | Requires Auth |
---|---|---|---|
POST | /users/ | Signs a user up and returns created user | |
POST | /users/login | Logs in a user and returns logged in user | |
GET | /users/profile | Get user profile | x |
Responsible for handling posts.
fields: ["_id", "title", "body", "tagList", "starCount", "comments", "author"]
Method | Path | Description | Requires Auth |
---|---|---|---|
GET | /posts | Gets all posts | x |
POST | /posts | Creats a new post | x |
PUT | /posts/:id | Update the post for the gievn id | x |
GET | /posts/:id | Get the post for the given id | x |
DELETE | /posts/:id | Remove the post for the given id | x |
https://moleculer.services/docs/0.13/moleculer-web.html
Each service has it's own database. So all access to persistent data must go through the API gateway and pass authorization.
The Moleculer framework has a built-in service discovery feature. But you don’t need to use any central service discovery tool (like Zookeeper, Consul, etcd) because it is integrated into the Moleculer protocol. This solution is a dynamic discovery. It means that the nodes don’t need to know all other nodes at starting
Moleculer has several built-in load balancing strategies. If services have multiple running instances, ServiceRegistry uses these strategies to select a node from all available nodes. For this project, I used the round-robin strategy. This strategy selects a node based on round-robin algorithm.
moleculer.config.js
// Settings of Service Registry. More info: https://moleculer.services/docs/0.13/registry.html
registry: {
// Define balancing strategy.
// Available values: "RoundRobin", "Random", "CpuUsage", "Latency"
strategy: "RoundRobin",
// Enable local action call preferring.
preferLocal: true
}
Moleculer has a built-in caching solution to cache responses of service actions. For this project, I used Redis. Simple In-Memory cacher could also be used.
moleculer.config.js
// Define a cacher. More info: https://moleculer.services/docs/0.13/caching.html
cacher: "Redis",
To communicate other nodes (ServiceBrokers), you need to configure a transporter. The most transporters connect to a central message broker server which is liable for message transferring among nodes. These message brokers mainly support publish/subscribe messaging pattern.
Transporter is an important module if you are running services on multiple nodes. Transporter communicates with other nodes. It transfers events, calls requests and processes responses …etc. If a service runs on multiple instances on different nodes, the requests will be load-balanced among live nodes. There are several transporters. RabitMQ, NATS,etc..
// More info: https://moleculer.services/docs/0.13/networking.html
transporter: "NATS",
The Moleculer has a built-in circuit-breaker solution. It is a threshold-based implementation. It uses a time window to check the failed request rate. Once the threshold value is reached, it trips the circuit breaker.
The Circuit Breaker can prevent an application from repeatedly trying to execute an operation that’s likely to fail. Allowing it to continue without waiting for the fault to be fixed or wasting CPU cycles while it determines that the fault is long lasting. The Circuit Breaker pattern also enables an application to detect whether the fault has been resolved. If the problem appears to have been fixed, the application can try to invoke the operation.
// Settings of Circuit Breaker. More info: https://moleculer.services/docs/0.13/fault-tolerance.html#Circuit-Breaker
circuitBreaker: {
// Enable feature
enabled: false,
// Threshold value. 0.5 means that 50% should be failed for tripping.
threshold: 0.5,
// Minimum request count. Below it, CB does not trip.
minRequestCount: 20,
// Number of seconds for time window.
windowTime: 60,
// Number of milliseconds to switch from open to half-open state
halfOpenTime: 10 * 1000,
// A function to check failed requests.
check: err => err && err.code >= 500
},
Bulkhead feature is implemented in Moleculer framework to control the concurrent request handling of actions.
https://docs.microsoft.com/en-us/azure/architecture/patterns/bulkhead Isolate elements of an application into pools so that if one fails, the others will continue to function.
This pattern is named Bulkhead because it resembles the sectioned partitions of a ship's hull. If the hull of a ship is compromised, only the damaged section fills with water, which prevents the ship from sinking.
// Settings of bulkhead feature. More info: https://moleculer.services/docs/0.13/fault-tolerance.html#Bulkhead
bulkhead: {
// Enable feature.
enabled: false,
// Maximum concurrent executions.
concurrency: 10,
// Maximum size of queue
maxQueueSize: 100,
},
Every request to any microservice must pass through the gateway and get authenticated via JWT.
An action that requires auth is marked with {auth: "required"}
Deploying microservices, with their interdependence, is much more complex process than deploying monolithic application. It is important to have fully automated infrastructure. We can achieve following benefits with Continuous Delivery approach:
The ability to release software anytime Any build could end up being a release Build artifacts once - deploy as needed Here is a simple Continuous Delivery workflow, implemented in this project:
In this configuration, Travis CI builds tagged images for each successful git push. So, there are always latest image for each microservice on Docker Hub and older images, tagged with git commit hash. It's easy to deploy any of them and quickly rollback, if needed.
npm run dev
: Start development mode (load all services locally with hot-reload & REPL)npm run start
: Start production mode (setSERVICES
env variable to load certain services)npm run cli
: Start a CLI and connect to production. Don't forget to set production namespace with--ns
argument in scriptnpm run lint
: Run ESLintnpm run ci
: Run continuous test mode with watchingnpm test
: Run tests & generate coverage reportnpm run dc:up
: Start the stack with Docker Composenpm run dc:down
: Stop the stack with Docker Composenpm run dctest:up
: Start the test stack with Docker Composenpm run dctest:down
: Stop the test stack with Docker Compose
GarriBlog is open source, and would greatly appreciate your help. Feel free to suggest and implement improvements.
Any web/mobile app developers?
These are just simple features. They are not law and final. Contribution to this section is highly welcomed.
-
Users
- create/login users
- implement refresh tokens
- implement logout by blacklisting tokens
- update account -> reset password
-
Posts
- CRUD posts
- star/unstar post
- should we keep post revisions?
- Should posts include media(pictures/video)?
-
Comments on posts
- CRUD comment.
- star/unstar comment
- for simplicity, do not reply comment
- should we keep comment revision?
-
Social
- follow/unfollow people
-
App metrics and service stats
-
Cache when ever possible
-
Unit test contributions are welcomed too :)