Symfony Bundle for DDD Foundation. Provides Messenger integration for Command/Query buses and a base Kernel for DDD projects.
composer require alexandrebulete/ddd-symfony-bundleAdd the bundle to your config/bundles.php:
return [
// ...
AlexandreBulete\DddSymfonyBundle\DddSymfonyBundle::class => ['all' => true],
];The bundle provides a DddKernel that automatically imports services, packages, and routes from your Bounded Contexts.
Extend DddKernel in your application's src/Kernel.php:
<?php
declare(strict_types=1);
namespace App;
use AlexandreBulete\DddSymfonyBundle\DddKernel;
class Kernel extends DddKernel
{
// You can override configureContainer/configureRoutes if needed
}The DddKernel automatically imports:
- Services:
src/*/Infrastructure/Symfony/config/services.{php,yaml} - Packages:
src/*/Infrastructure/Symfony/config/packages/*.{php,yaml} - Routes:
src/*/Infrastructure/Symfony/routes/*.{php,yaml}
This means each Bounded Context can define its own configuration without modifying the main config/ folder.
src/
├── Post/ # Bounded Context
│ └── Infrastructure/
│ └── Symfony/
│ ├── config/
│ │ ├── services.php # Auto-imported
│ │ └── packages/
│ │ └── doctrine.yaml # Auto-imported
│ └── routes/
│ └── api.yaml # Auto-imported
└── User/ # Another Bounded Context
└── Infrastructure/
└── Symfony/
└── config/
└── services.php # Auto-imported
The bundle automatically configures two Symfony Messenger buses:
command.bus- For write operations (Commands)query.bus- For read operations (Queries)
Handlers decorated with #[AsCommandHandler] or #[AsQueryHandler] are automatically registered to their respective buses.
use AlexandreBulete\DddFoundation\Application\Command\AsCommandHandler;
use AlexandreBulete\DddFoundation\Application\Command\CommandInterface;
readonly class CreatePostCommand implements CommandInterface
{
public function __construct(
public string $title,
public string $content,
) {}
}
#[AsCommandHandler]
readonly class CreatePostHandler
{
public function __invoke(CreatePostCommand $command): void
{
// Handle the command
}
}use AlexandreBulete\DddFoundation\Application\Command\CommandBusInterface;
use AlexandreBulete\DddFoundation\Application\Query\QueryBusInterface;
class PostController
{
public function __construct(
private CommandBusInterface $commandBus,
private QueryBusInterface $queryBus,
) {}
public function create(): Response
{
$this->commandBus->dispatch(new CreatePostCommand(
title: 'My Post',
content: 'Content...',
));
// ...
}
public function list(): Response
{
$posts = $this->queryBus->ask(new GetPostsQuery(
page: 1,
itemsPerPage: 10,
));
// ...
}
}The bundle provides an Autocomplete Symfony Form field powered by Stimulus + Tom Select (no jQuery).
It is designed for admin back-offices and DDD projects, and works with ID-based values (Value Objects friendly).
If your application uses Importmap / AssetMapper:
php bin/console importmap:require tom-select
php bin/console importmap:require tom-select/dist/css/tom-select.default.cssEnable the Stimulus controller in assets/controllers.json:
{
"controllers": {
"@alexandrebulete/ddd-symfony-bundle": {
"autocomplete": {
"enabled": true,
"fetch": "eager",
"path": "@alexandrebulete/ddd-symfony-bundle/autocomplete_controller",
"autoimport": {
"tom-select/dist/css/tom-select.default.css": true
}
}
}
}
}use AlexandreBulete\DddSymfonyBundle\Form\Type\AutocompleteType;
$builder->add('authorId', AutocompleteType::class, [
'label' => 'Author',
'remote_url' => $this->urlGenerator->generate('admin_user_autocomplete'),
'placeholder' => 'Search by email…',
'min_length' => 2,
]);The autocomplete endpoint must return:
{
"results": [
{ "id": "uuid-1", "text": "user@example.com" }
]
}| Option | Description |
|---|---|
| remote_url | AJAX endpoint URL |
| placeholder | Input placeholder |
| min_length | Minimum characters before search |
| limit | Maximum results returned |
| initial_text | Preselected label (edit forms) |
You can override the messenger configuration by creating your own config/packages/messenger.yaml:
framework:
messenger:
buses:
command.bus:
middleware:
- doctrine_transaction
query.bus: ~If you need custom container or routes configuration, override the methods:
<?php
declare(strict_types=1);
namespace App;
use AlexandreBulete\DddSymfonyBundle\DddKernel;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
class Kernel extends DddKernel
{
protected function configureContainer(ContainerConfigurator $container): void
{
parent::configureContainer($container);
// Add your custom imports here
$container->import($this->getProjectDir().'/config/custom/*.yaml');
}
}