yceruto/ddd-maker-bundle
A Symfony MakerBundle extension for scaffolding domain-driven design projects. It provides interactive commands to generate aggregates, entities, value objects, repositories, use cases, and other DDD building blocks, helping teams establish consistent layered architecture faster.
What it is. yceruto/ddd-maker-bundle extends Symfony's MakerBundle with generators for Domain-Driven Design building blocks — aggregates, commands, queries, events and their handlers. Instead of hand-writing scaffolding, you get make:ddd:* commands that drop classes into the correct layer with namespaces wired up.
Reality check for a Laravel dev. This is a Symfony bundle (Symfony 5.4/6.x, PHP 8.0+). It will not install into a Laravel app — no service provider, no Artisan integration. Use it in a Symfony project, or treat it as a reference design and hand-roll equivalent generators in Laravel.
Minimal steps.
composer require --dev yceruto/ddd-maker-bundle
composer require --dev symfony/maker-bundle
Confirm it's registered (Flex does this automatically; otherwise add it to config/bundles.php), then:
bin/console list make
Look for the make:ddd:* namespace — that's your entry point.
Configure layers first. The bundle is driven by a ddd_maker config block mapping logical layers to namespaces and paths:
# config/packages/ddd_maker.yaml
ddd_maker:
layers:
Domain:
namespace: App\Domain
path: src/Domain
Application:
namespace: App\Application
path: src/Application
Infrastructure:
namespace: App\Infrastructure
path: src/Infrastructure
Every generated class lands according to this map. Get it wrong and generated classes end up in the wrong namespace — the single most common source of frustration.
First use case. Pick a bounded context (e.g. Billing), sketch an aggregate, then generate a command + handler pair and wire them into Symfony Messenger. That one end-to-end slice teaches you the template style and how much editing it actually saves.
Where to look next: the bundle's src/Maker/ directory to see exactly what each maker produces, and its templates/ folder to understand what you can override.
Bounded context per directory. Treat each context as a top-level folder under every layer:
src/Domain/Billing/
src/Application/Billing/
src/Infrastructure/Billing/
Generated classes follow that split. Keep the domain folder pure PHP — no Symfony imports, no Doctrine annotations if you can avoid them.
Command → handler → bus. Typical flow:
messenger.yaml).Queries mirror commands, on a separate bus. Register a query.bus in Messenger so commands and queries don't share a transport — this is where DDD discipline pays off in Symfony and keeps the read/write split honest.
Events. Generate domain events as plain classes; listeners live in Application or Infrastructure. Publish synchronously via the EventDispatcher or asynchronously via a Messenger transport.
Generate top-down. Aggregate/entity → repository interface → command/query → handler → controller/console adapter. The makers fill in namespaces and use statements based on what already exists, so generating bottom-up leaves dangling references.
Template overrides. MakerBundle's convention applies: drop a template with the same relative path into your app's templates/ (set maker.template_dir), or override via bundle config. If a generated class doesn't match your team's style, fork the Twig template rather than editing every generated file.
Write your own makers. MakerBundle lets you register custom makers tagged maker.command. Subclass the bundle's makers to add conventions (e.g. auto-adding #[AsMessageHandler] or your own attribute). This is the clean extension path.
Testing. Generated handlers are plain services — unit-test them in isolation against an in-memory repository implementing the Domain interface. Reserve integration tests for the Infrastructure implementation.
Not Laravel-compatible. The biggest gap for a Laravel developer — no providers, no Artisan, no laravel/framework support. If you want the same idea in Laravel, write a small generator around your existing make: commands; the DDD shape this bundle enforces is worth copying verbatim.
Small user base, stale-ish. 17 stars, no known dependents, last release December 2023. It hasn't been battle-tested against edge cases. Pin symfony/maker-bundle and symfony/framework-bundle in composer.json, and commit your ddd_maker.yaml. Don't expect prompt upstream fixes.
Namespace/path drift. The ddd_maker.layers config must match PSR-4 autoloading exactly. A typo produces a class Symfony can't load and the error won't point at your config. Run composer dump-autoload -o after changing layers to surface problems early.
Generated code needs finishing. Makers produce skeletons — they don't wire constructors, properties, or Messenger routing for you. Budget a minute per class. Treat them as scaffolding, not codegen.
Layer enforcement is advisory. The bundle generates structure but doesn't stop you importing Symfony into Domain. Add qossmic/deptrac (or PHPStan/Psalm layer rules) to actually enforce boundaries — this is the missing half of every DDD maker.
Template overrides break silently. Overrides keyed by relative path stop applying after a MakerBundle upgrade renames a template. Re-check after every upgrade; there's no error, just a quiet revert to the default.
Dev-only dependency. Keep it under require-dev. Nothing should ship this to production — the bundle is a development aid and drags MakerBundle's dependencies along.
Debugging missing commands. If bin/console list doesn't show make:ddd:*, the bundle isn't registered, your Symfony version is too old, or MakerBundle isn't loaded (the DDD makers extend it). Check config/bundles.php, then bin/console cache:clear.
Prefer extension over forking. Use custom makers and template overrides. Forking a 17-star project is a maintenance liability you don't want to inherit.
How can I help you explore Laravel packages today?