# Creating three simple micro-services using NodeJS and deploying them using Docker + Kubernetes

### About Application

#### Description

This is a microservices-based application built using Node.js and MongoDB. The application consists of three microservices - User, Product, and Order - each of which is deployed as a separate Docker container. The User service is responsible for managing user data, the Product service is responsible for managing product data, and the Order service is responsible for managing order data.

All three services communicate with the same MongoDB database, but each has its own collection.

The User service makes use of the Product service and Order service to allow users to add products to their wishlist and confirm their order. The Product service is independent and does not use any other services. The Order service stores references to the IDs of the user and products associated with each order.

All services are containerized using Docker and communicate with each other over a private network. The application can be deployed and scaled easily by spinning up multiple instances of each service as needed.

In this application, Swagger has been used to provide detailed descriptions of each endpoint in each microservice. This documentation makes it easy to understand how to use the API, and it reduces the amount of time required for developers to read and understand the source code.

The Swagger documentation for each microservice is available through the Swagger UI, which is a web-based tool that provides a user-friendly interface for interacting with the API. The Swagger UI is accessible through a web browser by visiting the appropriate URL for each microservice.

The Swagger documentation for the User service is available at [http://localhost:3000/api-docs/,  
The](http://localhost:3000/api-docs/,%EF%BF%BCThe) Swagger documentation for the Product service is available at [http://localhost:4000/api-docs/](http://localhost:4000/api-docs/). Similarly, the Swagger documentation for the Order service is available at [http://localhost:5001/api-docs/](http://localhost:5001/api-docs/).

> ***NOTE:*** *PORT 3000, 4000, 5001 are accessible when the the services are run locally using nodejs. For the docker containers these ports are exposed and mapped to PORT 3001, 3002, 3003 respectively. More about this is mentioned in the section where dockerfiles are explained.*

#### API Endpoints

**The User service provides the following endpoints:**

* **GET** /users - retrieves all users from the database
    
* **GET** /user/:id - retrieves a user by ID
    
* **POST** /user - creates a new user in the database
    
* **PUT** /user/:id - updates a user by ID
    
* **DELETE** /user/:id - deletes a user by ID
    
* **POST** /user/wishlist/:userId/:productId - adds a product to the wishlist of the specified user
    
* **POST** /user/order/:userId - confirms the order for the products in the wishlist of the specified user
    

**The Product service provides the following endpoints:**

* **GET** /products - retrieves all products from the database
    
* **GET** /product/:id - retrieves a product by ID
    
* **POST** /product - creates a new product in the database
    
* **PUT** /product/:id - updates a product by ID
    
* **DELETE** /product/:id - deletes a product by ID
    

**The Order service provides the following endpoints:**

* **GET** /orders - retrieves all orders from the database
    
* **GET** /order/:id - retrieves an order by ID
    
* **GET** /orders/user/:userId - retrieves all orders for the specified user
    
* **POST** /order - creates a new order in the database
    
* **DELETE** /order/:id - deletes an order by ID
    

### Relationship between the services

**A. UserService**

The User service is responsible for managing user data, including creating new users, updating user information, and deleting users. In addition to these standard user management functions, the User service provides the ability for users to add products to their wishlist and confirm their order. These additional functions rely on the Product and Order services.

**B. Product Service**

The Product service is an independent service that manages product data, including creating new products, updating product information, and deleting products. It does not rely on any other services and can operate independently.

**C. Order Service**

The Order service is responsible for managing order data, including creating new orders and deleting orders. The Order service stores references to the IDs of the user and products associated with each order. When a user confirms their order using the User service, the Order service is called to create a new order and store the appropriate user and product IDs.

**D. Interservice Communication**

All services are containerized using Docker, which provides a lightweight, portable way to package and deploy the services. The services communicate with each other over a private network, which ensures that the communication between services is secure and isolated from the outside world.

Because the services are containerized and communicate over a private network, the application can be easily scaled up or down as needed. Additional instances of each service can be spun up to handle increased traffic or demand, and the services can be scaled back down when demand decreases. This allows the application to remain performant and responsive, even under heavy load.

### Database and Strategy

MongoDB is used in this application as the primary database for storing and managing data across all three microservices - **User**, **Product**, and **Order**. MongoDB is a NoSQL document-oriented database that provides flexible and scalable storage for unstructured data. In this application, MongoDB is used to store data in a **collection** format, with each collection representing a specific data type - user, product, or order.

Each microservice uses a dedicated MongoDB collection to store and retrieve data. When a new record is created in a microservice, it is stored as a document in the appropriate MongoDB collection. The document format allows the data to be easily structured, indexed, and queried. The use of MongoDB as the primary database provides several benefits for this type of application:

**1\. Flexible data structure:** MongoDB allows for flexible and dynamic data structures, which is particularly useful in applications where data requirements may change frequently.

**2\. Scalability**: MongoDB is designed to scale easily and can handle large volumes of data and high write loads.

**3\. High availability:** MongoDB supports automatic failover and data replication, ensuring that data remains available even in the event of hardware failures or other issues.

4\. **Performance:** MongoDB is designed to provide high performance and low latency, making it well-suited for use in applications where speed is critical.

In addition to providing a primary database for the application, MongoDB also provides a rich set of features and tools for managing and analyzing data. For example, MongoDB provides a powerful query language that allows for complex queries and aggregations, as well as a variety of indexing options to optimize performance. MongoDB also provides a range of monitoring and management tools that can be used to optimize database performance, diagnose issues, and ensure high availability.

### **Dockerfile configuration (per service)**

```javascript
// userservice dockerfile

FROM node:14
WORKDIR /
COPY package*.json ./
RUN npm install
COPY . .
EXPOSE 3000
CMD ["npm", "start"]
```

```javascript
// productservice dockerfile

FROM node:14
WORKDIR /
COPY package*.json ./
RUN npm install
COPY . .
EXPOSE 4000
CMD ["npm", "start"]
```

```javascript
// orderservice dockerfile

FROM node:14
WORKDIR /
COPY package*.json ./
RUN npm install
COPY . .
EXPOSE 5001
CMD ["npm", "start"]
```

### Deployment Steps

> ***Since all the three services are build in NodeJS, the deployment and testing steps are common for all. In this documentation we will be seeing the steps for User Service. Following the same steps the other two services can be tested as well.***

1. **Local NodeJS Server**
    

* set the current working directory to `users/`
    
* `$ npm run dev`
    

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1684768086895/92b9a7a4-26a7-4688-a86e-eda8df2a2268.png align="center")

* Server listening on [http://localhost:3000](http://localhost:3000)
    
* Check [http://localhost:3000/api-docs/](http://localhost:3000/api-docs/) for API documentation powered by Open API (swagger)
    

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1684768134329/8f2428bd-db3f-4538-b296-ad3e9847f4f3.png align="center")

* Testing `GET` endpoint using Postman
    

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1684768187213/b976481f-c585-413c-af22-553ca030c109.png align="center")

* Similarly, all other endpoints for user service or other two services can be tested following the Open API docs or using the endpoints mentioned in the introduction of this documentation report.
    

1. **Docker Image and Container**
    
      
    **Every microservice in a different container:**
    

* Create a new docker image for the services from the respective working directories:
    
* `$ docker build -t userservice .`  
    `$ docker build -t productservice .`  
    `$ docker build -t orderservice .`
    

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1684768350304/e1db81fe-790e-4480-afe1-ae4cf89ecb1f.png align="center")

* Run the Docker container with the image from the respective working directories: `$ docker run -p 3001:3000 userservice`  
    `$ docker run -p 3002:4000 productservice`  
    `$ docker run -p 3003:5001 orderservice`
    

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1684768385827/e1afff01-1ca8-430a-a2cd-a072c3f3e55d.png align="center")

* Containerized services can be tested for userservice, productservice and orderservice respectively, by hitting the following endpoints:  
    [http://localhost:3001/api-docs/  
    http://localhost:3002/api-docs/](http://localhost:3001/api-docs/￼http://localhost:3002/api-docs/)
    
    [http://localhost:3003/api-docs/](http://localhost:3003/api-docs/)  
      
    **Every microservice grouped under a single network:**
    

* Create a new network:  
    `$ docker network create <name>`  
    `$ docker network create micronetwork`
    
* If the images are not already created for each of the services create them using: `$ docker build -t userservice .`
    
    `$ docker build -t productservice`
    
    `$ docker build -t orderservice .`
    

*  Create a new `docker-compose.yaml` file to orchestrate the three services to run together under the same network named `micronetwork`
    

```yaml
version: '3.8'

services:
  userservice:
    image: userservice
    container_name: userservice
    networks:
      - micronetwork
    ports:
      - "3001:3000"

  productservice:
    image: productservice
    container_name: productservice
    networks:
      - micronetwork
    ports:
      - "3002:4000"

  orderservice:
    image: orderservice
    container_name: orderservice
    networks:
      - micronetwork
    ports:
      - "3003:5001"
      
networks:
  micronetwork:
    driver: bridge
```

* Run docker-compose in the repository where the compose file exists:  
    `$ docker-compose up -d`
    

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1684768620206/e7eabb5c-5ba1-4a53-ac77-598e762a9d37.png align="center")

* Containerized services can be tested for userservice, productservice and orderservice respectively, by hitting the following endpoints:  
    [http://localhost:3001/api-docs/  
    http://localhost:3002/api-docs/](http://localhost:3001/api-docs/￼http://localhost:3002/api-docs/)
    
    [http://localhost:3003/api-docs/](http://localhost:3003/api-docs/)
    

1. **Container deployment with Kubernetes**
    

Deploy the microservice container images to docker hub by doing the following:

**User service:**

* `$ docker tag userservice adeeshsharma/userservice`
    
* `$ docker push adeeshsharma/userservice`
    
    **Product Service:**
    
* `$ docker tag productservice adeeshsharma/productservice`
    
* `$ docker push adeeshsharma/productservice`  
    
     **Order Service:**
    
* `$ docker tag orderservice adeeshsharma/productservice`
    
* `$ docker push adeeshsharma/productservice`
    

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1684768800234/e185d3f1-db2b-4bd6-a4fe-23ae299e993d.png align="center")

* Create 2 new `yaml` files for Kubernetes deployment and service configuration (has to be done for all 3 services but for this example we will only be doing it for the userservice)
    

```yaml
// userservice-deployment.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: userservice-deployment
  labels:
    app: userservice
spec:
  replicas: 1
  selector:
    matchLabels:
      app: userservice
  template:
    metadata:
      labels:
        app: userservice
    spec:
      containers:
        - name: userservice
          image: adeeshsharma/userservice
          ports:
            - containerPort: 3000
```

```yaml
// userservice-service.yaml

apiVersion: v1
kind: Service
metadata:
  name: userservice
spec:
  selector:
    app: userservice
  type: LoadBalancer
  ports:
    - protocol: TCP
      port: 5000
      targetPort: 3000
      nodePort: 31110
```

*  Apply these configurations by running the following:  
    `$ kubectl apply -f k8s/userservice-deployment.yaml`
    
    `$ kubectl apply -f k8s/userservice-service.yaml`
    
* Open minikube dashboard and see the status
    

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1684768977848/a96a5cae-a329-4519-b866-e4c688c3ceef.png align="center")

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1684768989917/fb67e460-ce49-4db4-8e21-25c9ea41a454.png align="center")

* Use `$ kubectl get services` to get all the running service details
    

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1684769027149/f0e07e2a-c521-4fd5-ae10-10b615caf7c3.png align="center")

* Run `$ minikube service userservice` to fire up the service
    

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1684769118072/8fd811a7-0d09-4e95-b6a0-ce2a5919c66b.png align="center")

* The service starts running on [http://127.0.0.1:56444/](http://127.0.0.1:56444/)
    
* Test the service using [http://127.0.0.1:56444/api-docs/](http://127.0.0.1:56444/api-docs/)
    

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1684769142986/8ff6907b-c23b-4de6-af82-76213b2914c3.png align="center")

* The user service is now running using docker image used from dockerhub and orchestrated by kubernetes
    

> ***NOTE:*** *Repeat the same steps with respective configuration yaml files for productservice and orderservice to get the entire application up and running user kubernetes and docker images.*

### Links

* **User service**: [https://github.com/adeeshsharma/scalableservices-user](https://github.com/adeeshsharma/scalableservices-user)
    
* **Product Service**: [https://github.com/adeeshsharma/scalableservices-product](https://github.com/adeeshsharma/scalableservices-product)
    
* **Order Service**: [https://github.com/adeeshsharma/scalableservices-order](https://github.com/adeeshsharma/scalableservices-order)
    
* ***Docker Hub link:*** [*https://hub.docker.com/repositories/adeeshsharma*](https://hub.docker.com/repositories/adeeshsharma￼Drive)
    
* [***Drive***](https://hub.docker.com/repositories/adeeshsharma￼Drive) ***Link:*** [*https://drive.google.com/drive/folders/12MONI4iUVvsymEH-jfM862RF2yz9oWNH?usp=sharing*](https://drive.google.com/drive/folders/1lGV4KimWW1vkk8U6lvTAwW_jYyIqXtAV?usp=sharing)
    

> ***NOTE****: docker-compose.yaml resides in the user service repository*
