starker-xp / cqrses-bundle
A demo bundle
Installs: 7
Dependents: 0
Suggesters: 0
Security: 0
Stars: 0
Watchers: 1
Forks: 0
Open Issues: 0
Type:symfony-bundle
pkg:composer/starker-xp/cqrses-bundle
Requires
- php: >=5.3.0
This package is not auto-updated.
Last update: 2026-02-15 01:29:13 UTC
README
Archive — Ce projet est un bundle Symfony expérimental développé dans le cadre d'une démarche de R&D personnelle autour du Domain-Driven Design (DDD), du CQRS (Command Query Responsibility Segregation) et de l'Event Sourcing. Il n'est plus maintenu et est conservé à titre de référence.
Contexte
Ce bundle a été conçu à une époque où je me formais au DDD en lisant des ouvrages de référence (notamment ceux d'Eric Evans et de Vaughn Vernon) tout en travaillant sous Symfony 2.x/3.x. L'objectif était double :
- Apprendre par la pratique — Implémenter de A à Z les patterns CQRS et Event Sourcing pour en comprendre les mécanismes internes.
- Fournir un scaffolding — Générer automatiquement la structure de code CQRS/ES pour un domaine métier donné, afin de réduire le boilerplate.
Analyse rétrospective réalisée en 2026 dans le cadre d'un nettoyage et d'une mise en archive de mes dépôts GitHub/GitLab.
Architecture
Le bundle implémente le cycle complet CQRS/Event Sourcing :
┌─────────────────────────────────────────────┐
│ WRITE SIDE │
│ │
Requête ──► CommandBus ──► CommandHandler ──► Domain Aggregate │
│ │ │
│ DomainEvent(s) │
│ │ │
│ ┌─────────┴─────────┐ │
│ ▼ ▼ │
│ EventStore Projection │
│ (append-only) (read model) │
└─────────────────────────────────────────────┘
┌─────────────────────────────────────────────┐
│ READ SIDE │
│ │
Requête ──► QueryBus ──► QueryHandler ──► ReadRepository ──► DB │
└─────────────────────────────────────────────┘
Structure du code
├── Command/ # Commandes Symfony (CLI) pour le scaffolding
│ ├── GenererBaseCQRSCommand.php # Génère la structure CQRS/ES complète d'un domaine
│ └── GenererCommandeCommand.php # Génère une commande CQRS individuelle
│
├── DependencyInjection/ # Intégration Symfony (chargement config/services)
│
├── Generator/
│ └── CQRSESGenerator.php # Moteur de génération basé sur les templates Twig
│
├── Resources/
│ ├── config/services.yml # Déclaration de l'EventStore comme service
│ └── views/ # 21 templates Twig pour le scaffolding
│ ├── Services/Command/ # Templates Command + Handler (Créer, Modifier, Supprimer)
│ ├── Services/Domain/ # Templates Agrégat, Events, DTO, Collection
│ ├── Services/Persistence/ # Templates Repository (écriture) + Projection
│ ├── Services/Query/ # Templates Query + QueryHandler
│ └── Resources/config/ # Template de déclaration de services YAML
│
└── Services/ # Le framework CQRS/ES réutilisable
├── Bus/ # Bus abstrait + interface Handler
├── Command/ # CommandBus, CommandInterface, CommandHandlerInterface
├── Domain/ # DomainEvents (agrégat), AbstractEvent, EventInterface, etc.
├── Persistence/ # EventStore, AbstractProjection, AggregateHistorique
└── Query/ # QueryBus, QueryInterface, QueryHandlerInterface
Patterns et concepts implémentés
CQRS — Séparation Lecture / Écriture
- CommandBus : dispatche une commande vers son handler via une convention de nommage (
XxxCommand→XxxHandler) - QueryBus : dispatche une query vers son handler pour la lecture
- Séparation des connexions BDD :
write_connection,read_connection,event_connection
Event Sourcing
- DomainEvents (agrégat) : accumule les events en mémoire, les applique pour muter l'état interne
- EventStore : persiste les events dans une table
events(append-only) - Reconstitution : reconstruit un agrégat en rejouant ses events depuis l'EventStore
- Snapshots : sauvegarde périodique de l'état de l'agrégat (tous les 5 events) pour optimiser la reconstitution
- Projections : met à jour un read model dénormalisé de manière synchrone après chaque commit
Scaffolding
Pour un domaine Produit, la commande génère automatiquement :
php bin/console starkerxp:cqrs:generer:structure MonBundle Produit
Fichiers générés :
Command/Produit/— CreerProduitCommand, CreerProduitHandler, ModifierProduitCommand/Handler, SupprimerProduitCommand/HandlerDomain/Produit/— ProduitDomain (agrégat), ProduitDTO, ProduitPOPO, ProduitCollection, Events (AEteCree, AEteModifie, AEteSupprime)Persistence/Ecriture/Produit/— ProduitRepository, ProduitProjectionPersistence/Lecture/— ProduitRepository (read-only)Query/Produit/— ProduitQuery, ProduitQueryHandler, ProduitListerQuery, ProduitListerQueryHandlerResources/config/— services.produit.yml (déclaration DI complète)
Compétences démontrées
- Domain-Driven Design : agrégats, domain events, value objects, repositories
- CQRS : séparation stricte des responsabilités lecture/écriture avec bus dédiés
- Event Sourcing : persistance par events, reconstitution, snapshots, projections
- Symfony : création de bundle, DependencyInjection, commandes console, générateur de code
- Architecture logicielle : interfaces, classes abstraites, conventions, séparation en couches
Limitations connues
Ce projet étant un exercice de R&D, certaines limitations ont été identifiées avec le recul :
| Limitation | Détail |
|---|---|
| Pas de tests | Aucun test unitaire ou fonctionnel n'accompagne le code |
| Sérialisation PHP | L'EventStore utilise serialize()/unserialize() au lieu de JSON, ce qui pose des problèmes de sécurité et d'interopérabilité |
| Pas de gestion de concurrence | Absence d'optimistic locking sur l'EventStore (pas de vérification de version au commit) |
| Projections synchrones | Les projections sont exécutées de manière synchrone dans la même transaction, ce qui ne scale pas |
| Convention de nommage fragile | Le mapping Command→Handler repose sur un remplacement de chaîne dans le nom de classe |
| Compatibilité Symfony | Conçu pour Symfony 2.x/3.x — incompatible avec Symfony 4.2+ (ContainerAwareCommand déprécié, SensioGeneratorBundle abandonné, TreeBuilder API modifiée) |
Prérequis (historiques)
- PHP >= 5.3
- Symfony 2.x ou 3.x
- Doctrine DBAL
- SensioGeneratorBundle
- rhumsaa/uuid (aujourd'hui ramsey/uuid)
Licence
Projet personnel — usage libre à titre de référence.