This Symfony 7 boilerplate shows CQRS and event sourcing end to end
Symfony 7 DDD ES CQRS backend boilerplate.
At a glance
- What is it?
- The symfony-7-es-cqrs-boilerplate is a running example of Domain-Driven Design, CQRS and event sourcing on Symfony 7 and PHP 8.3, with an event store, projections, Messenger workers and a Swagger API. Its value is that every layer is wired up and executable, and its cost is a stack that needs MySQL, Elasticsearch and RabbitMQ to run.
- Who is it for?
- Adopt this boilerplate as a reference implementation when you are learning CQRS and event sourcing on Symfony, since every layer is wired and runnable rather than described in prose, and the documentation covers the buses, use cases, projections and async processing. Do not adopt it as the base of a production service without reading the event store and projection code carefully, because the example use cases are a sign up, sign in, change email and logout.
- 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 74 days ago.
- What is it written in?
- Mainly PHP, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.
Editorial analysis
It is a working example, not a diagram
The repository describes itself as a boilerplate demonstrating Domain-Driven Design, Command Query Responsibility Segregation and Event Sourcing patterns using Symfony 7 and PHP 8.3, and the implementation checklist makes that concrete. Every box is ticked: an environment in Docker, Symfony Messenger, an event store, a read model, async event subscribers, a REST API, a web UI, a REST API over the event store, and Swagger API documentation. The web UI is described in the list as having a terrible user interface, which is a useful piece of honesty, because it tells you the effort went into the backend structure and not into the presentation. The example use cases are the standard account lifecycle: sign up, change email, sign in and logout, which is deliberately small enough to read end to end.
The stack is heavier than the pattern needs
The stack listing is PHP 8.3, Symfony 7, MySQL 8.0, Elasticsearch and Kibana 7.11.0, and RabbitMQ 3. The presence of Elasticsearch and Kibana is what makes this more than a CQRS example, since it is there to serve the read model and to make the projections inspectable. That is a legitimate way to demonstrate the pattern, since a read model backed by a document store shows the decoupling between the write side and the read side more clearly than a second relational table would. It also means you cannot run the example without a search cluster, which is the main thing to weigh before you spend an afternoon on it. The RabbitMQ broker and the Symfony Messenger workers then carry the asynchronous side, with the compose file defining separate consumers for the `events` and `users` transports.
The workers are two Messenger consumers with limits set
The compose file shows how the asynchronous processing is wired, and the detail worth copying is the resource limits. There is a `workers_events` service running the console with `messenger:consume events` in verbose mode with a memory limit of 256M and a time limit of 3600, and a `workers_users` service doing the same for the `users` transport. Both have a restart policy of unless-stopped, and both wait for MySQL, RabbitMQ and Elasticsearch to report healthy before starting. The time limit of 3600 is the part that matters in practice, because a long-running worker that leaks memory will eventually be killed and restarted, and a worker with a memory limit is killed earlier than that. Splitting the two transports into separate services means a slow projection cannot starve the user-facing consumers.
The Makefile is the entry point for everything
The project setup table is short enough to memorise, and every entry is a make target rather than a composer script. Setup is `make start`, tests are `make phpunit`, static analysis is `make phpstan`, code style is `make cs`, there is a PHP shell at `make s=php sh`, Xdebug is toggled with `make xoff` and `make xon`, and artifacts are built with `make artifact`. The Windows note is the practical detail, because Docker behaves differently there: you may need to change `docker-os=` to `docker-os=windows` in the Makefile, or pass it on the command line as `make start docker-os=windows`. The repository also carries the usual static analysis configuration files, with phpstan.dist.neon, psalm.xml, phpcs.xml.dist, easy-coding-standard.yml and a deptrac.yaml for layer enforcement.
SSH into the container instead of using the IDE's Docker integration
The PHPStorm section explains a workaround that will interest anyone who has fought this before. PHPStorm has native integration with Docker Compose, which is convenient but stops the PHP container after the test scenario runs, and that is not acceptable when you are using fpm. The author's solution is to use an SSH connection instead, and the reasoning is about speed rather than ideology, since running a separate container purely for the IDE is much slower. The warning is emphatic: SSH into the container is only for that reason and only in the dev tag, and if you have SSH installed in your production container you are doing it wrong. The connection details are in a table, with the host as `localhost` for Docker 4 on Mac or the output of `dinghy ip` for Dinghy, on port 2323, with the project path mapped to `/app` inside the container.
A branch per Symfony major, and the project has moved on
The default branch is `symfony-7`, and the repository is described as a long living one that started at version 4 and was upgraded to each major version since then. Older majors live on their own branches, with a `symfony-6` branch, a `symfony-5` branch and a `symfony-4` branch. That is worth knowing before you clone, because the README badges and some of the documentation links still point at the symfony-6 repository, so you will see a mix of names as you read. The last push to the current branch was on 2026-08-09, the repository has no GitHub releases, and the licence is MIT with the file in the root. The project has been around long enough to have contributors listed through the all-contributors specification, five of them named in the README.
The documentation is the part you actually need
The doc directory is where the teaching happens, and the file list is a good syllabus. There is a document on command and query buses, one on creating use cases, one on adding projections, and one on async processing, plus a UI workflow document and a Kubernetes deployment document, and a page on Xdebug configuration. The order of those files is the order in which the pattern makes sense, because the buses are the entry point, use cases are what the buses carry, projections are what consume the events, and async processing is what connects the two. The Kubernetes deployment document is notable for a boilerplate of this kind, since most example projects stop at a compose file, and it is also where you would look to find out how much of the local Docker setup is genuinely portable.
Editorial conclusion
Adopt this boilerplate as a reference implementation when you are learning CQRS and event sourcing on Symfony, since every layer is wired and runnable rather than described in prose, and the documentation covers the buses, use cases, projections and async processing. Do not adopt it as the base of a production service without reading the event store and projection code carefully, because the example use cases are a sign up, sign in, change email and logout. Verify first that you can run the three backing services locally, that the event store and read model schema suits your needs, and that the branch you use matches the Symfony version you are on, since older majors live on separate branches.
Frequently asked questions
What does the Symfony 7 ES CQRS boilerplate demonstrate?
It demonstrates Domain-Driven Design, CQRS and event sourcing with Symfony 7 and PHP 8.3, including an event store, a read model, Symfony Messenger, async event subscribers, a REST API, an event store REST API and Swagger documentation.
How do I start the Symfony 7 ES CQRS boilerplate?
Run `make start`. The other Makefile targets are `make phpunit` for tests, `make phpstan` for static analysis, `make cs` for code style, `make s=php sh` for a PHP shell, `make xoff` and `make xon` for Xdebug, and `make artifact` to build artifacts.
What services does the boilerplate need running?
The stack is PHP 8.3, Symfony 7, MySQL 8.0, Elasticsearch and Kibana 7.11.0, and RabbitMQ 3. The compose file defines workers that consume the events and users Messenger transports and that wait for MySQL, RabbitMQ and Elasticsearch to be healthy first.
Where are the older versions of this boilerplate?
The project started at version 4 and was upgraded to each major version since, so the symfony-4, symfony-5 and symfony-6 branches hold the earlier versions. The current branch is symfony-7.
How does the boilerplate handle debugging with PHPStorm?
The author avoids PHPStorm's native Docker Compose integration because it stops the PHP container after a test run, and uses an SSH connection to the container instead, on port 2323 with the project path mapped to /app. SSH access is only for the dev tag, not production.
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/jorge07-symfony-7-es-cqrs-boilerplate)Was this useful?