Nest CQRS

Introduction

The domain-driven design consists of four layers, each consisting of interface, application, domain, and infrastructure.

Domain Layer


The domain layer is the core layer in domain-driven design and the highest level layer that does not depend on any layer.

Here, a model consisting of aggregate, entity, and vo is implemented, and the model has core business logic inside.

In addition, domain events generated by business logic execution and the repository interface of the domain model are defined here.

The repository basically manages a collection of domain objects.

In addition, with a separated interface pattern, the interface of the repository exists in the domain layer and the implementation is in the infrastructure layer.

Here, when creating a domain object, the factory pattern is used to use the factory.

Events defined here are called domain events and are issued when domain logic is executed, and domain events are received by domain event handlers in the application layer and execute logic according to the event.

Application Layer


The application layer depends on the domain layer and executes the domain logic of the domain model.

In addition, domain events generated by executing domain logic are handled through domain event handlers.

Integration events that are issued to services in external contexts are defined here, domain events received by domain event handlers are processed, and these integration events are published through the integration event publisher.

In addition to the integration event publisher, adapters for external communication are defined here as interfaces.

This allows you to configure integration with other services of msa in a synchronous or asynchronous manner.

In the application layer, the implementation of cqrs inside the service is shown.

You define command and query objects, and implement handlers that execute them.

Command and query are implemented separately, and the handler that handles them is also implemented and executed separately.

The point to note here is that access to the domain is done only by command, so all events are triggered by command.

Each command and command handler creates a domain object or gets it from the repository, executes the domain logic, and stores it through the repository.

On the other hand, query is processed through query handler, but executes only the query defined as interface without accessing the domain and returns the result.

Interface Layer


In the interface layer, it depends on the application layer, generates necessary commands and queries and executes them through the bus.

This layer defines interfaces provided to users or other services, receives http, message, etc., verifies the request and returns a response if necessary.

Infrastructure Layer


The infrastructure layer is responsible for implementing detailed technology at each other layer.

These can be technical contents such as repository in domain, publisher, adapter in application, controller in interface, etc.

Examples include database implementation and access, communication or messaging systems, caching or external communication technologies.

By having all of these detailed implementations in the infrastructure layer, you can isolate technical changes so that changes do not spread to other parts.

Please refer to the image below.[image source]

Screenshot 2020-12-15 at 11 32 25 PM

Getting started

This is REST api made by node.js, nest, redis, mysql with typescript.

So you have to get node.js environment, redis for cache, mysql for database, know typescript syntax.

Prerequisites


Please install node.js and I recommend to use docker for your database.

My recommand node.js version is dubnium and latest docker version.

Create development environment

First, clone this repository into your local environment. Run followed command in your terminal.

  git clone https://github.com/kyhsa93/nestjs-rest-cqrs-example.git

Second step, install package that needed in this project.

If your node.js environment is successly downloaded, you can use node package manager.

Run followed command in your terminal.

  npm install

Next up, generate mysql and redis.

If you already have mysql, redis in your development environment, you can use that.

But if you don't have one or both, try this process.

Install docker for your OS from link in top of this documentation.

And run followed command.

If your docker is successfully installed, you can use docker cli.

  docker run --name nest -d -p 3306:3306 -e MYSQL_DATABASE=nest -e MYSQL_ROOT_PASSWORD=test -v ~/database/nest:/var/lib/mysql mysql:5.7
  docker run --name redis -d -p 6379:6379 redis:alpine

  OR

  docker-compose -f docker-compose.development.yml up -d # create mysql, redis container for development environment
  docker-compose -f docker-compose.development.yml down  # remove created containers

And then, you can connect mysql in http://localhost:3306, user name 'root' and password is 'test'.

Finaly, your develop environment is created.

You can start api with followed command.

  npm start

And if you modify code and save, you can see the process detect code changes and restart it self.

Start with docker

If you can use docker cli, you can build docker image.

  docker build -t nest-sample
  docker images # list up docker images

And then you can create and run docker container using builded image.

  docker run -d -p 5000:5000 nest-sample
  docker ps # list up running container

and now you can connect api through http://localhost:5000.

Start with docker compose

Docker compose in this project is include api redis and mysql 5.7 for database.

Run followed command in project directory.

  docker-compose up -d # build images, create and run containers in background

If container is created, you can access api on http://localhost:5000.

And you can access database through http://localhost:3306.

Default database user is root and password is test.

If you want apply your modified code into the running container, you can add build option.

  docker-compose up -d --build # if source code is changed, rebuild image, recreate and start container

After use compose, you have to stop and remove containers.

  docker-compose down # stop and remove container in compose

Start with kubernetes

If you want to use kubernetes, you can use manifest.yaml for apply to your kubernetes cluster.

Use minikube for create kubernetes locally or use your own kubernetes. (docker for desktop can be enable local cluster)

Minikube: https://kubernetes.io/docs/setup/learning-environment/minikube/

  kubectl apply -f manifest.yaml  # create kubernetes resource in your kubernetes using manifest.yaml file
  kubectl delete -f manifest.yaml # delete kubernetes resource using manifest.yaml file

When all Deployment, Service and Pods is created, you can use api through http://localhost.

Also swagger ui address is http://localhost/api.

If you want see all container in deployment, you can use kubectl.

  kubectl get all # print all kubernetes default namespace
  kubectl logs deployment.apps/nestjs-rest-cqrs-example --all-containers=true
  kubectl logs deployment.apps/nestjs-rest-cqrs-example --all-containers=true -f

About kubernetes: https://kubernetes.io/

Start with helm

Helm can help you to manage kubernetes applications.

Helm Charts help you define, install, and upgrade even the most complex Kubernetes application.

  helm install --name <releasename> helm # create helm chart
  helm delete --purge <releasename> # delete helm chart

About helm: https://helm.sh/

Configuration

All configuration is in src/app.config.ts

Most default configuration can use through you environment values.

And also you can modify configurations.

Documentation

Documentaion about this project is made swagger.

Start this api and connect http://localhost:5000/api in your browser.

Scripts

  git clone https://github.com/kyhsa93/nestjs-rest-cqrs-example.git # clone this project

  # run mysql database container
  docker run --name nest -d -p 3306:3306 -e MYSQL_DATABASE=nest -e MYSQL_ROOT_PASSWORD=test -v ~/database/nest:/var/lib/mysql mysql:5.7

  docker build -t nest-sample # build docker image named nest-sample

  docker images # list up docker images

  docker run -d -p 5000:5000 nest-sample # run docker container using image nameed nest-sample (host port 5000 is mapped container port 5000 and container run background process)

  docker ps # list up running container

  docker-compose up -d # build images, create and start containers in background

  docker-compose up -d --build # if container image's change exists, rebuild image, recreate and restart container

  docker-compose down # stop and remove container in compose

  npm install   # install packges
  npm test      # run test
  npm run build # transpile typescript
  npm start     # run sample code

  docker-compose up # use compose
  
  kubectl apply -f manifest.yaml  # create kubernetes resource in your kubernetes using manifest.yaml file
  kubectl delete -f manifest.yaml # delete kubernetes resource using manifest.yaml file

  kubectl get all # print all kubernetes default namespace
  kubectl logs deployment.apps/nestjs-rest-cqrs-example --all-containers=true
  kubectl logs deployment.apps/nestjs-rest-cqrs-example --all-containers=true -f

  helm install --name <releasename> helm # create helm chart
  helm delete --purge <releasename> # delete helm chart