I used this application to learn the fundamentals of microservices. I implemented 4 different services using an event-driven architecture and a small React frontend. Docker and Kubernetes was used for containerization and orchestration. For more details about design and implementation choices, see below.
Table of Contents:
Overview of Application and Services
Solution 1: Synchronous Communication
Solution 2: Asynchronous Communication Using an Event Bus and Query Service
Tech used:
- NodeJS
- Express
- React
- Docker
- Kubernetes
- Ingress-Nginx (creates both a Load Balancer and an Ingress)
- Skaffold (for updating code in running pod and creating/deleting k8s objects)
Architecture:
- Microservice
Goal: Create new post
Path: /posts
Method: POST
Body: { title: string }
Goal: Get all posts
Path: /posts
Method: GET
Body: none
Goal: Create a comment associated with the given post ID
Path: /posts/:id/comments
Method: POST
Body: { content: string }
Goal: Retrieve all comments associated with the given post ID
Path: /posts/:id/comments
Method: GET
Body: none
The moderation service will consume events as they are submitted by the user. It wil first receive a ComentCreated event, use a terniary operator to determine if a particular word is found in the content property (in this case, the word 'guac'; I don't like guac ;) ). If so, it will update the status property to rejected. If not, the status property will be set to approved. Next, it will emit a CommentModerated event to the Event Broker which will then pass it on to the Query Service.
Here is the gist of the problem we are dealing with. It's really a matter of inefficiency. Below you picture of the blog in its current state with posts and comments add:
If we look at the list of request made to the comments service we see that seven GET requests were made to retrieve the post comments:
This is our current dilemma: we are making mulitple requests to one service when we could just make one. And this is where an event-driven microservice architecture would come in handy:
- Query service has zero dependencies on other services
- Query service wil be extremely fast
- More difficult to understand
- Data duplication
There are several implementations, including:
- RabbitMQ
- Kafka
- NATS...
In this app, I'll build a very basic Event Bus from scratch using Express. This is not going to be a production-grade bus.
Whenever a POST request is made to the Post Service, Comments Service, or Query Service, the particular service will then forward that request to the Event Bus. In turn, the Event Bus will then forward the request with all of its data to all 3 services while at the same time storing the event. That's it. As I said, this is a very basic implementation. A more thorough, production-grade implementation will come in a future project.
Obviously, this does not deal with the data duplication problem. But that is not the intention here. There are tools available to address this issue.
This is obviously a small application and in the real world this architecture choice would be overkill. But for the sake of learning, I also chose to use Docker for two primary reasons:
- Running this app make some pretty big assumptions about our environment. These include the assumption the Node is installed on the system
- Running this app requires precise knowledge of how to start it (npm start)
In a real-world app of this type, complexity would be compounded. In order to use this app for learning purposes, I opted to containerize them with Docker, which will make it easier for anyone wanting to run the app on their own machines. Docker wraps every dependency needed to run the application in a nice little box.
- Tells Kubernetes about the different Deployments, Pods, and Services (referred to as 'Objects') that I will create.
- Written in YAML syntax
- I will always store these files with our project source code because they are a form of documentation
The Load Balancer Service will reach out to its provider and provision a load balancer. It gets traffic into a single pod. Technically speaking, we will also have an ingress controller which will have a set of routes to direct traffic to different services. The config file used to provision the load balancer has a set of instruction to reach out to some provider (Google, Asure, Amazon, etc) and it is that provider which will provision the load balancer for us.