nestjs-boilerplate-microservice: DDD, CQRS and event sourcing on NestJS
Nestjs Microservice boilerplate: apply DDD, CQRS, and Event Sourcing within an event driven architecture
At a glance
- What is it?
- nestjs-boilerplate-microservice is a NestJS 10 and TypeScript skeleton for event driven services that applies DDD, CQRS and event sourcing, with one shell script that brings up MySQL, EventStore, Adminer and the application in a private Docker network.
- Who is it for?
- nestjs-boilerplate-microservice fits a team that has decided on DDD, CQRS and event sourcing and wants a working NestJS 10 skeleton with EventStore, MySQL and TypeORM already wired together, rather than assembling that stack by hand.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 2 days ago.
- What is it written in?
- Mainly TypeScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
A skeleton for teams that already chose event sourcing
The repository describes itself as a microservice boilerplate based on NestJS v10, built with TypeScript, and the project description is more specific: it applies DDD, CQRS and event sourcing within an event driven architecture. That framing sets expectations correctly. This is not a framework and it is not a library of reusable patterns; it is a starting repository with the pieces of that style already assembled, so that the first service you write does not begin with an afternoon of wiring. The topic list adds the usual vocabulary of the field, including saga, GraphQL and event store, and the package itself is named demo-microservice-boilerplate and marked private, which is a candid signal that the name of the package is a sample rather than something to publish. The licence is MIT, the last push to the repository was on 2026-10-18, and the guidelines document in docs/ is the place the README points for how the pieces are meant to be used.
One build script starts the whole stack
The containerised path is the advertised one, and it is genuinely one command:
(cd scripts/ && sh build.sh)The README then lists what that script does, and the list is the real documentation of the local topology. It creates a private network for the containers, configures and deploys a MySQL container, an Adminer container, an EventStore container and the nestjs-boilerplate container, and connects all of them to that private network. It also enables hot reloading by mounting the working directories inside the application container, and it creates and mounts data directories for MySQL and EventStore so the data survives a restart, under $HOME/mysql-data-dir and $HOME/eventstore-data-dir. So the same command gives you a database, an event store, a database admin UI and the service, wired together, with the code mounted for live editing.
The ports and the sample credentials are fixed in the scripts
Three addresses matter once the stack is up. The application home page is at http://localhost:3000, the EventStore admin interface is at http://localhost:2113, and the Adminer interface is at http://localhost:8080. The EventStore instance comes with the credentials admin and changeit, and the Adminer form is filled with the server sql-db, the user root, the password root and the database demo-db. To change any of the defaults you edit the script at script/dev/evVAr.sh, which is where the ports and the container configuration live. Those credentials are development values with no protection, and the ports are published on the host, so the stack should stay on a development machine or behind a firewall. Getting into the application container itself is a plain docker exec, using the container name the script creates:
sudo docker exec -it devtest bashTearing the stack down is symmetric. A soft cleanup runs the cleanup script, which removes the private network and stops and removes the MySQL, Adminer and EventStore containers while keeping the data directories, and a hard cleanup adds the -h or --hard flag, which also deletes the MySQL and EventStore data directories.
EventStore projections are off by default, so no events arrive
There is one line in the local setup section that explains a class of confusing bug, and it is worth reading before anything else. In order to start receiving events from EventStore, you have to enable projections, and they are disabled by default. This is an EventStore behaviour rather than a quirk of the boilerplate, but it interacts badly with an event sourced application: the service starts cleanly, connects to the store, and simply never sees an event, because nothing has been told to project the streams. The same mechanism is why the Docker path lists an EventStore container at all. The lesson generalises past this project, because any team moving from a CRUD service to an event sourced one will hit the same silent default.
Running locally: npm scripts for each mode and TypeORM migrations
The local path expects you to bring the infrastructure yourself, with a running EventStore, a running MySQL server, a running Node and npm. The package engines ask for Node 16.0.0 or newer and npm 6.0.0 or newer. Installation is npm install, and the database is set up through the TypeORM CLI with the ormconfig file at the repository root. The npm scripts cover the four ways to run the service, which is a sensible default for a TypeScript service:
# development
$ npm run start
# watch mode
$ npm run start:devUnder the hood the start script runs src/main.ts through ts-node with path mapping, the watch mode uses tsc-watch and starts dist/main.js on success, the debug mode starts it under the Node inspector with a break, and the production mode runs the compiled dist output. There are dedicated scripts for the TypeORM migration commands, generate, run, revert and create, plus schema:drop, and a test script for unit tests, one for end to end tests and one for coverage.
The .env file is where the configuration lives
The repository ships a .env.example that lists the configuration surface, and reading it is the fastest way to understand the runtime shape. There is the process configuration, NODE_ENV, HOST on 0.0.0.0, a PORT of 4000 and a separate TRANSPORT_PORT of 5000, which is the microservice transport listener rather than the HTTP API. A group of SWAGGER_ keys sets the path, title, description, version and scheme for the generated API documentation. The MySQL group carries the host, port, credentials and database, and the EventStore group carries the protocol, hostname, a TCP port of 1113, an HTTP port of 2113 and the admin credentials. Finally there is an AWS group for S3, with an access key, a secret key and a bucket name. The presence of a bucket name in a file that also holds the sample EventStore password tells you how much of this is a working example rather than a security posture.
A four stage Dockerfile that ends as a non-root user
The image is worth reading as a deployment sketch. It starts from node:16-alpine3.17, adds git, creates a working directory and chowns it to the node user, and switches to that user, so the final image runs as a non-root user. There is a dependencies stage that copies only the package files and installs, a build stage that copies the source, the tsconfig files and ormconfig.js and runs the build script, a polishing stage that installs production dependencies only, and a release stage that assembles the tree from the earlier stages and sets the command:
FROM node:16-alpine3.17 AS release
WORKDIR /app
CMD ["node","dist/main.js"]The repository also carries a Jenkinsfile, a makefile, renovate.json for dependency updates and a semantic-release configuration, which tells you the intended deployment and release path: a CI job builds the image, and a release is cut from the tag.
Where this boilerplate is the wrong tool
The obvious case is a team on a current runtime. The package asks for Node 16, the container is built on node:16-alpine3.17, and the install guidance for the local path points at a MySQL 5.7 manual page. A project on a newer Node release will spend its first hour on version friction rather than on event sourcing. The second case is a team looking for a maintained reference implementation. There is exactly one tagged release, v1.0.0, and it dates from 2019, while the repository itself saw a push on 2026-10-18, so the code moves but the releases do not. The third case is a team that does not want event sourcing at all, or wants a different one. The alternative approach in that situation is to start from the plain NestJS application structure and add one pattern at a time, CQRS first if you need it, with a relational database and an outbox table rather than an event store. The difference in approach is the point: this boilerplate gives you a working event sourced stack in one command, and charges you for it with the abstractions and the infrastructure you did not ask for.
Editorial conclusion
nestjs-boilerplate-microservice fits a team that has decided on DDD, CQRS and event sourcing and wants a working NestJS 10 skeleton with EventStore, MySQL and TypeORM already wired together, rather than assembling that stack by hand. It does not fit a project on a current Node release, since the package asks for Node 16.0.0 or newer and the container image is built on node:16-alpine3.17, and it does not fit a team that wants a maintained reference implementation, since the only tagged release is v1.0.0 from 2019. Before adopting it, read the guidelines document, enable the EventStore projections before expecting events, and check that the sample credentials, admin and changeit for EventStore and root and root for Adminer, are changed before the stack is exposed anywhere.
Frequently asked questions
How do I start the nestjs-boilerplate-microservice stack with Docker?
Run (cd scripts/ && sh build.sh). The script creates a private container network and deploys MySQL, Adminer, EventStore and the application, with hot reloading through mounted working directories and data directories under $HOME for MySQL and EventStore.
Why does the application not receive any events?
Because projections in EventStore are disabled by default, and the documentation says you have to enable them before the application starts receiving events. Without that step the service connects to the store and stays silent.
Which database and migration tooling does the boilerplate use?
It uses MySQL with TypeORM, and the database is set up through the TypeORM CLI using the ormconfig file at the repository root. The npm scripts cover migration:generate, migration:run, migration:revert and migration:create, plus schema:drop.
What do I need to run the project outside Docker?
A running EventStore, a running MySQL server, a running Node server and npm. The package engines ask for Node 16.0.0 or newer and npm 6.0.0 or newer, and the install step is npm install.
How do I tear down the Docker environment?
A soft cleanup runs (cd scripts/ && sudo sh cleanup.sh), which removes the private network and the MySQL, Adminer and EventStore containers. Adding the -h or --hard flag also deletes the MySQL and EventStore data directories.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/0xb4lamx-nestjs-boilerplate-microservice)Was this useful?