CodelyTV php-ddd-example: A Symfony 7 Reference for Hexagonal Architecture, DDD, and CQRS
GitHub describes it as 🐘🎯 Hexagonal Architecture + DDD + CQRS in PHP using Symfony 7. The repository metadata lists PHP as its primary language. This article stays within the project description and details documented in the GitHub repository README.
At a glance
- What is it?
- This repository is a teaching-focused PHP application that demonstrates Domain-Driven Design, CQRS, and Hexagonal Architecture in Symfony 7. It is a reference implementation, not a production framework, and its value lies in how it structures code for learning.
- Who is it for?
- Adopt this repository if you are a developer or team learning DDD, CQRS, or Hexagonal Architecture in PHP and want a concrete, working example to study. Do not use it as a production template: it is a teaching artifact with no release history, no explicit license, and a deliberately simple codebase that omits many production concerns.
- Can I use it commercially?
- Not without permission. GitHub finds no licence file in the repository, and without a licence all rights are reserved by default: you may read the code but not reuse it. Check the README, or ask the authors, before using it.
- Is it still maintained?
- Probably not. The repository last received commits 26 months ago, on August 6, 2024.
- 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.
Star history
- CodelyTV/php-ddd-example3,152 ★ · +8
| Repository | 2026-08-13 | 2026-10-22 |
|---|---|---|
| CodelyTV/php-ddd-example | 3.1k | 3.2k |
8 Stars in the last 70 days, measured from the daily GitHub snapshot. Compare with other projects
Editorial analysis
What This Repository Actually Solves
The problem this project addresses is the gap between reading about DDD and CQRS and seeing them applied in a working PHP codebase. Most tutorials show isolated examples, but this repository provides a full application: a MOOC platform with two bounded contexts, Mooc and Backoffice. It is aimed at developers who want to understand how to structure a PHP application around domain logic rather than framework conventions. The README explicitly says it is an example to 'take a look, play and have fun with', which signals its teaching purpose. It is not a library or a framework, it is a reference implementation. The target audience is someone who already knows PHP and Symfony basics but wants to see how aggregates, repositories, and buses fit together in a realistic project.
Project Structure: Bounded Contexts and Modules
The repository organizes code by bounded context, then by module, then by layer. The tree in the README shows src/Mooc/Videos with Application, Domain, and Infrastructure folders. Each module inside a context follows this pattern. The Mooc context represents the public platform, while Backoffice handles customer support use cases. This separation is intentional: it prevents domain concepts from leaking across contexts. Inside each module, the Application layer contains actions like Create, Find, Trim, and Update. Each action has a Command, a CommandHandler, and a service class like VideoCreator. This structure mirrors what you would see in a DDD book, but in runnable PHP. The README claims the code is 'decoupled from any framework', yet it includes Symfony implementations, so the decoupling is about domain code not importing framework classes, not about avoiding Symfony entirely.
The Domain Layer: Aggregates and Repositories
The domain layer is where the core business logic lives. Aggregates extend a base AggregateRoot class, as shown in the Course example. The repository pattern is deliberately minimal: the README states repositories usually have only two methods, search and save. If more complex queries are needed, they add a searchByCriteria method using the Specification or Criteria pattern. This is a deliberate simplification. Many real-world repositories have more methods, but this constraint forces you to think in terms of domain needs rather than data access convenience. The CourseRepository interface and its Doctrine implementation are given as examples. This approach makes the domain independent of persistence technology, which is the essence of Hexagonal Architecture. However, it also means you have to write more boilerplate for every new query, which is a trade-off you should accept before adopting this pattern.
Buses: Command, Query, and Event
The project implements three types of buses. The Command Bus and Query Bus both use the Symfony Message Bus, with in-memory implementations. The Event Bus has three variants: an in-memory Symfony bus, a MySQL-based bus that uses pulling, and a RabbitMQ bus that requires the RabbitMQ C extension. This variety is instructive because it shows how the same domain event can be delivered synchronously or asynchronously depending on infrastructure. The README notes that every time a domain event is published, it is exported to Prometheus for monitoring. This is a concrete example of how events can trigger side effects without coupling the domain to the monitoring system. The trade-off is that switching between in-memory and RabbitMQ buses requires different infrastructure setup, and the MySQL bus with pulling is not a typical production choice for high-throughput systems.
Getting It Running: Docker and Make Commands
The setup is Docker-first. You need Docker installed, then you clone the repository and run `make build` to install dependencies and bring up the project. This gives you three applications: a Mooc backend on port 8030, a Backoffice backend on port 8040, and a Backoffice frontend on port 8041. Each has a health-check endpoint. For tests, you run `make deps` first, then `make test` to execute PHPUnit and Behat tests. The environment configuration is done by copying `.env` to `.env.local` if you need to modify parameters. This is a straightforward setup for a developer familiar with Docker and Make. The README does not list the PHP version or Symfony version explicitly beyond the description mentioning Symfony 7, so you should check the composer.json in the repository for exact requirements. The reliance on Docker means you need to have Docker installed and running, which can be a barrier on some systems.
Limitations and When This Is the Wrong Tool
This repository is not a production template. It has no release history, no explicit license in the metadata, and the README lists missing items like Swagger documentation. The code is simplified to teach concepts, so it lacks many production concerns: authentication, authorization, error handling, logging, and deployment configuration are not covered in the README. If you need a real application to run in production, this is the wrong starting point. The in-memory buses are fine for learning but not for distributed systems. The MySQL event bus using pulling is unusual and may not scale. The repository is also tied to Symfony 7, so if you use Laravel or another framework, you would need to adapt the patterns significantly. The README mentions 'some Symfony and Laravel implementations', but the actual code shown is Symfony-centric. For a production project, you would need to add many layers of robustness that this example deliberately omits.
Alternatives and How They Differ
A common alternative to this repository is a framework-agnostic DDD skeleton, such as the one from the PHP DDD community, which often uses a similar structure but without a specific framework. The difference is that those skeletons focus on pure domain logic and let you plug in any framework, whereas this example uses Symfony's Message Bus for its command, query, and event buses. Another alternative is to use a Symfony bundle like Ecotone or Prooph, which provide production-ready messaging infrastructure. Ecotone, for instance, gives you a full CQRS and event sourcing framework with support for multiple transports, whereas this repository shows a hand-rolled, minimal implementation. The trade-off is between learning the underlying concepts by reading simple code versus using a robust library that handles edge cases. If you want to see how DDD works in a real system with minimal abstraction, this repository is better. If you want a battle-tested messaging layer, a dedicated library is the way to go.
Maintenance and License Considerations
The repository has no recent releases and the last push date is unknown in the provided material. This suggests it may not be actively maintained, which is a risk if you plan to base your learning on it. The license is listed as unknown, which is a legal concern. Without a clear license, you cannot legally reuse the code in your own projects without asking the authors. The README links to CodelyTV's paid courses, indicating that this is a promotional asset as much as a codebase. The maintenance cost for you is low: you clone it, run it, and study it. But if you want to adapt it, you must verify the license and be prepared to maintain any changes yourself. The code depends on Symfony 7 and Docker, so you need to keep those up to date. The monitoring setup with Prometheus is a nice touch but adds complexity if you just want to read the code. In short, treat this as a learning resource, not a dependency.
Editorial conclusion
Adopt this repository if you are a developer or team learning DDD, CQRS, or Hexagonal Architecture in PHP and want a concrete, working example to study. Do not use it as a production template: it is a teaching artifact with no release history, no explicit license, and a deliberately simple codebase that omits many production concerns. Before relying on it, verify the license with the authors, check the last commit date and PHP version compatibility, and confirm that the Symfony 7 and Docker setup matches your environment. If you need a production-grade starting point, consider a framework-agnostic DDD skeleton or a maintained Symfony bundle instead. The repository's real value is as a map of how to organize bounded contexts, modules, and buses, not as a deployable product.
Frequently asked questions
What is CodelyTV/php-ddd-example?
A PHP example application built around domain-driven design, a hexagonal structure organised by modules, and separate command, query and event buses, written to stay as simple as possible. The README says it is decoupled from any framework while also shipping Symfony and Laravel implementations.
How do I run the php-ddd-example project?
You need Docker, then a make target that installs dependencies and starts the stack. Three applications come up, a course platform backend, a back office backend and a back office front end, each with a health check path on its own port.
How are the php-ddd-example tests run?
Through make targets that execute inside the running containers: unit tests per suite for the course platform, the shared code and the back office, plus behaviour tests for the course platform backend. The documented flow installs dependencies with a separate target first.
Which data stores does php-ddd-example start?
A message broker with its management interface, a relational database started with an empty root password and empty passwords explicitly permitted, a search engine running as a single node with its security feature disabled, and a metrics store that receives every published domain event.
Is php-ddd-example still being worked on?
The repository is not archived, but the last push on the default branch is dated 2024-08-06 and there are no published releases. The README itself notes that things are missing, naming an API specification and better documentation, and offers guidelines on request.
How does php-ddd-example split reads from writes?
Events have three bus implementations, in memory, a relational store used with polling, and a broker reached through a C extension. The command side has a single in-memory implementation and the query side a single in-memory implementation, so the durable paths are on events only.
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/codelytv-php-ddd-example)Star history chart for your README
[](https://hysenlabs.com/projects/codelytv-php-ddd-example)