Building Microservices - Using Node - Js With DDD, CQRS, and Event Sourcing - Part 2 of 2
Building Microservices - Using Node - Js With DDD, CQRS, and Event Sourcing - Part 2 of 2
By using Medium, you agree to our Privacy Policy, including cookie policy.
You have 2 free stories left this month. Sign up and get an extra one for free.
tl;dr: I aim to help you learn and apply CQRS and event sourcing using a modern approach.
Part 1:
Commands ow diagram
Microservices should completely align with their bounded contexts that represent a
given domain — and are to be loosely-coupled and highly cohesive.
In Part 1, we discussed the DDD, CQRS, and event sourcing patterns that are used in my
example project. Hopefully, you had some time to check out the GitHub repository and
clone it as well. In this part, we will cover the implementation in detail — I also
recommend you go beyond the scope of this article and learn more about the concepts
applied here as well as other related concepts.
Let us break down the project’s microservice architecture with a flow chart:
To make Medium work, we log user data. By using Medium, you agree to our Privacy Policy, including cookie policy.
the domain model implements an aggregate root and sits at the heart of a
microservice —it also validates and persists events on behalf of the aggregate using
command handlers
events are atomically persisted to the event store before the handling of a given
event
As an added benefit of using DDD, we can create easy to understand directory tree
structures. Let us take a look at the project’s directory tree…
To make Medium work, we log user data. By using Medium, you agree to our Privacy Policy, including cookie policy.
Note: Create a new file called .env in the root directory by copying the given .env.example
file.
Node.js was picked as the technology of choice since Node.js follows a modularized
approach to building applications — it fits well with the nature of microservices as well
as the Unix philosophy:
Nest.js was picked as the framework of choice since the Nest.js framework libraries
follow best practices and promote good design patterns such as dependency injection.
These patterns improve code quality and maintainability — and allow for evolvability.
Check out Nest’s documentation and learn about its fundamentals before continuing.
Here is what the AppModule that imports the UsersModule looks like…
AppModule — src/app.module.ts
Note: The AppModule is used to register all modules used by the microservice. In my project,
the modules that are applied include UsersModule and EventStoreModule.
UsersModule — src/users/users.module
To makeNote: The
Medium UsersModule
work, is used
we log user data. to bootstrap
By using Medium, all
youUsers domain-specific
agree to our Privacy Policy,logic.
including cookie policy.
User Requests
As of the time of writing this article, Fastify handles approx. 76,835 req/sec and Express
handles approx. 50,933 req/sec. Fastify has been implemented in my project since we
want the maximum throughput possible. You can easily change from Fastify to any other
router framework of choice.
The user creation flow is the most involved in this project so let’s take a look at a quick
flow diagram of the process of creating a new user…
User creation ow
Users trigger commands via user interfaces. Upon the UserCreatedEvent event being
captured, a userCreated saga has been implemented to continue the transaction of
welcoming the user as a way of onboarding the user.
Commands are created by the client application and then sent to the domain layer.
Commands are messages that instruct a specific entity to perform a certain action. (source)
To make Medium work, we log user data. By using Medium, you agree to our Privacy Policy, including cookie policy.
The domain model is where domain rules are defined. Users are to be described as an
aggregate instead of a domain model — in DDD, an aggregate is a cluster of domain
objects that can be treated as a single unit. Remember, domain model objects in our case
are immutable and therefore aggregate.
The aggregate forms a tree or graph of object relations. The aggregate root is the “top” one,
which speaks for the whole and may delegates down to the rest. It is important because it is
the one that the rest of the world communicates with. (source)
To make Medium work, we log user data. By using Medium, you agree to our Privacy Policy, including cookie policy.
User — src/users/models/user.model.ts
UserRepository — src/users/repository/user.repository.ts
Note: The Users repository uses the Users model to persist and handle events.
Concurrency, in the context of event sourcing, is an important thing to be aware of. With
distributed command handlers, concurrent access to or the modification of aggregates is
not always a problem — you can implement some sort of conflict resolution if it is.
http://localhost:1113 (tcp)
http://localhost:2113 (http)
All HTTP requests made to Event Store are idempotent — unless the expected version is
ignored.
Publishing events to the event store are made easy by the following code:
To make Medium work, we log user data. By using Medium, you agree to our Privacy Policy, including cookie policy.
In the project’s event-store.ts file, it contains logic for the Event Store bridge which
subscribes to any events in a given domain category stream. In this case, the category
stream is the Users domain — it uses a category projection called $ce-users .
Event Store comes with an interface out-of-the-box. Anytime an event occurs, the
interface will allow us to know of and interact with the event in real-time.
An event handler should be responsible for writing to materialized state or cache for
faster reads.
Finally, the heroic User saga continues the User creation transaction…
UsersSagas — src/users/sagas/users.saga.ts
After the UserCreatedEvent event is stored, the system fires the UserWelcomedEvent
event triggered by the userCreated saga — the user creation transaction is completed
thereafter.
qas/examples-nodejs-cqrs-es-swagger
A Node.js CQRS/ES Swagger API Microservice Example - qas/examples-nodejs-
cqrs-es-swagger
github.com
$ ./scripts/up.sh ## to boot up
$ ./scripts/down.sh ## to shut down
Conclusion
To makeEvents
Mediumallow us log
work, we to focus on By
user data. theusing
domain instead
Medium, of database
you agree schemas,
to our Privacy cross-team
Policy, including cookie policy.
communication is made easier because of it.
We have just completed the overview of building a decent starter microservice while
explaining the concepts of DDD, CQRS, and event sourcing. Clone the GitHub repository
and play with the project yourself.
Remember that all patterns have pros and cons and the CQRS and event sourcing
patterns are not exempt from that — by no means a silver bullet.
licensing platforms
database systems
Below, you will find a link to the project as well as my contact info.
Project’s GitHub
Resources
1. Martin Fowler — CQRS (article)
. . .
Going further
Fork the project and add your own custom denormalization logic — remember to share your
fork in the comments section below! You can also try and replay the events to construct the
view if you prefer.
Read related books and other material, learn about the different diagramming methods
for designing microservices using the links below, create your own event store
implementation, and learn to use an API gateway to protect your microservices.