# MiniShop DDD Blue Book Boilerplate Document de reference pour un boilerplate DDD minimaliste, pense pour PHP/Symfony, mais transposable. ## 1. But du boilerplate Construire une base unique, reutilisable pour des tutoriels progressifs sur les patterns du Blue Book: - `OHS` (Open Host Service) - `ACL` (Anti-Corruption Layer) - `Conformist` - `Published Language` Contraintes voulues: - un seul repo - `2 a 3 Bounded Contexts` maximum - interfaces explicites entre contextes - integration volontairement naive au debut - evolution pas a pas, concept par concept ## 2. Positionnement et principes Ce boilerplate privilegie la purete tactique/strategique du DDD, pas la sophistication technique. Principes directeurs: 1. Un `Bounded Context` = un modele autonome + un langage propre. 2. Aucune entite/VO partagee entre BC par defaut. 3. Les relations entre BC sont explicites via une `Context Map`. 4. Le code inter-BC passe par des contrats (DTO, messages, API), jamais par les modeles internes. 5. Tout pattern (Conformist, ACL, OHS, Published Language) est rendu visible dans l'arborescence et dans les tests. 6. Les choix techniques Symfony/Doctrine restent remplaçables. ## 3. Domaine MiniShop (scope minimal) ### 3.1 Bounded Contexts retenus - `Sales` (coeur metier): prise de commande - `Invoicing`: emission de facture - `LegacyFulfillment`: expedition via systeme legacy volontairement "sale" `Shipping` peut remplacer `LegacyFulfillment` dans une variante "moderne", mais la version pedagogique recommande `LegacyFulfillment` pour illustrer `ACL`. ### 3.2 Scenario fil rouge 1. Un client passe une commande dans `Sales`. 2. `Sales` notifie les autres BC qu'une commande est confirmee. 3. `Invoicing` produit une facture. 4. `LegacyFulfillment` cree une demande d'expedition dans un format legacy. ## 4. Ubiquitous Language par BC ### 4.1 Sales - Entites/Aggregats: `Order` - Value Objects: `OrderId`, `CustomerId`, `Money`, `OrderLine` - Evenements de domaine: `OrderPlaced`, `OrderConfirmed` - Invariants: - une commande a au moins une ligne - total > 0 - confirmation impossible si commande vide ### 4.2 Invoicing - Entites/Aggregats: `Invoice` - Value Objects: `InvoiceId`, `BillingParty`, `InvoiceLine`, `TaxRate` - Evenements de domaine: `InvoiceIssued` - Invariants: - une facture refere une commande externe - montant facture coherent avec ses lignes ### 4.3 LegacyFulfillment - Entites/Aggregats: `ShipmentRequest` - Value Objects: `LegacyOrderRef`, `ShippingAddress`, `ParcelSpec` - Evenements de domaine: `ShipmentRequested` - Contraintes: - format legacy impose (champs abrégés, codifications date/status) - mapping explicite depuis le langage publie ## 5. Context Map cible (pedagogique) Etat initial (naif): - `Sales` appelle directement des services applicatifs externes - schemas ad hoc, non versionnes - couplage fort et fragile Etat cible (apres tutoriels): - `Sales` expose un `OHS` pour consultation/commande (option REST ou message) - `Sales` publie un `Published Language` versionne (`sales.v1`) - `Invoicing` suit `Conformist` sur ce langage publie - `LegacyFulfillment` introduit un `ACL` entre langage publie et modele legacy Representation synthese: ```text Sales (Upstream) ├── Published Language (sales.v1.*) ├── OHS (query/command API) ├──> Invoicing (Conformist) └──> LegacyFulfillment (ACL) ``` ## 6. Architecture de code recommandee ```text . ├── apps/ │ └── symfony/ │ ├── config/ │ └── public/ ├── src/ │ ├── Sales/ │ │ ├── Domain/ │ │ ├── Application/ │ │ ├── Infrastructure/ │ │ └── Interfaces/ │ ├── Invoicing/ │ │ ├── Domain/ │ │ ├── Application/ │ │ ├── Infrastructure/ │ │ └── Interfaces/ │ ├── LegacyFulfillment/ │ │ ├── Domain/ │ │ ├── Application/ │ │ ├── Infrastructure/ │ │ └── Interfaces/ │ └── Shared/ │ └── Technical/ # utilitaires purement techniques (Clock, UUID) ├── contracts/ │ └── sales/ │ └── v1/ │ ├── Event/ │ └── Api/ ├── tests/ │ ├── Unit/ │ ├── Integration/ │ └── Contract/ └── docs/ ├── MINISHOP_DDD_BLUEBOOK_BOILERPLATE.md └── tutorials/ ``` Regle stricte: - `src/Shared` ne contient aucun concept metier transverse. - tout objet dans `contracts/` est stable, versionnable et "publique" entre BC. ## 7. Convention de couches par BC - `Domain`: - modeles metier, invariants, services de domaine, evenements de domaine - aucune dependance framework - `Application`: - use cases (commands/queries), orchestration, ports (interfaces) - `Infrastructure`: - persistance, bus, clients HTTP, adaptateurs externes - `Interfaces`: - HTTP CLI Messenger, serializers, controleurs ## 8. Contrats inter-BC (Published Language) Le contrat publie minimal recommande: - `contracts/sales/v1/Event/OrderPlaced.php` - `contracts/sales/v1/Event/OrderConfirmed.php` - `contracts/sales/v1/Api/OrderView.php` Regles: 1. Contrats immutables. 2. Version explicite (`v1`, puis `v2`). 3. Compatibilite retro geree par ajout, pas mutation destructive. 4. Tests de contrat obligatoires (`tests/Contract`). ## 9. Evolution tutorielle (roadmap) ### Episode 00 - Squelette + integration naive - BC en place - workflow `PlaceOrder -> Invoice -> LegacyShipment` - appels synchrones directs inter-BC - aucun Published Language officiel Tag Git: `step/00-naive` ### Episode 01 - Published Language - extraction des DTO publics dans `contracts/sales/v1` - premiere version de messages d'integration - tests de schema/serialisation Tag Git: `step/01-published-language` ### Episode 02 - Conformist (Invoicing) - `Invoicing` consomme tel quel `sales.v1` - documentation explicite de la dependance upstream - tests de non-regression de compatibilite Tag Git: `step/02-conformist` ### Episode 03 - ACL (LegacyFulfillment) - creation d'une couche `AntiCorruption` cote `LegacyFulfillment` - mapping `sales.v1 -> legacy command model` - isolation des bizarreries legacy dans l'ACL Tag Git: `step/03-acl` ### Episode 04 - OHS (Sales) - exposition d'un service hote explicite (`/api/sales/v1/...` ou endpoint message) - documentation des points d'entree supportes - modeles internes toujours non exposes Tag Git: `step/04-ohs` ### Episode 05 - Hardening - outbox/event relay (optionnel selon niveau tuto) - idempotence des consommateurs - monitoring minimal (logs de correlation) Tag Git: `step/05-hardening` ## 10. Definition of Done par episode Chaque episode doit fournir: 1. code compilable et testable 2. diagramme context map a jour (`docs/tutorials/`) 3. note "ce qui change" + "pourquoi Blue Book" 4. tag git pour reset rapide ## 11. Strategie de reset pour les tutos Approche recommandee: - une branche `main` stable (etat final ou etat de base choisi) - des tags immuables `step/xx-*` - une branche de travail recreee a chaque tuto depuis le tag Exemple: ```bash git fetch --tags git switch -C play/episode-03 step/03-acl ``` ## 12. Regles de purete Blue Book (checklist) - Le modele de domaine de chaque BC est independant des autres BC. - La relation upstream/downstream est documentee. - Les dependances de code suivent la context map, pas l'inverse. - Les traductions de langage sont localisees (ACL), jamais diffuses partout. - Les contrats publies sont versionnes et testes. - Les cas d'usage ne contournent pas les invariants d'agregat. ## 13. Gabarits minimaux utiles (pseudo-code) ### 13.1 Port de publication d'evenement (Application) ```php interface SalesEventPublisher { public function publishOrderConfirmed(OrderConfirmedMessage $message): void; } ``` ### 13.2 ACL vers legacy (Infrastructure) ```php final class LegacyShipmentAcl { public function fromSalesOrderConfirmed(OrderConfirmedMessage $message): LegacyCreateShipmentCommand { // Mapping explicite du langage publie vers le modele legacy. } } ``` ### 13.3 Use case de Sales (Application) ```php final class ConfirmOrderHandler { public function __invoke(ConfirmOrderCommand $command): void { // Charge l'agregat, applique l'invariant, persiste, publie l'evenement d'integration. } } ``` ## 14. Decoupage recommande des tests - `tests/Unit/Sales/Domain/*`: invariants d'agregat - `tests/Unit/Invoicing/Domain/*`: regles de facturation - `tests/Unit/LegacyFulfillment/Infrastructure/AntiCorruption/*`: mapping ACL - `tests/Contract/Sales/v1/*`: stabilite du langage publie - `tests/Integration/*`: wiring Symfony, bus, persistence ## 15. Ce que ce boilerplate n'essaie pas de faire - couvrir tous les patterns strategiques DDD d'un coup - fournir une architecture microservices complete - optimiser prematurement la performance La priorite est la clarte pedagogique, la reproductibilite et la fidelite Blue Book. ## 16. Specification minimale "codegen-ready" ### 16.1 Sales - cas d'usage Commandes: - `PlaceOrder` - `ConfirmOrder` - `CancelOrder` Queries: - `GetOrderById` - `ListOrdersByCustomer` Ports sortants: - `OrderRepository` - `SalesEventPublisher` - `Clock` - `TransactionManager` Evenements d'integration publies: - `sales.v1.OrderPlaced` - `sales.v1.OrderConfirmed` - `sales.v1.OrderCancelled` ### 16.2 Invoicing - cas d'usage Commandes: - `IssueInvoiceForExternalOrder` - `MarkInvoiceAsSent` Queries: - `GetInvoiceByOrderRef` Ports sortants: - `InvoiceRepository` - `InvoiceNumberGenerator` Evenements d'integration consommes: - `sales.v1.OrderConfirmed` (Conformist) ### 16.3 LegacyFulfillment - cas d'usage Commandes: - `RequestShipmentFromSalesOrder` - `MarkShipmentDispatched` Queries: - `GetShipmentByExternalOrderRef` Ports sortants: - `ShipmentRequestRepository` - `LegacyFulfillmentGateway` Evenements d'integration consommes: - `sales.v1.OrderConfirmed` (via ACL) ## 17. Matrice d'integration inter-BC | Producteur | Contrat | Consommateur | Pattern | Notes | |---|---|---|---|---| | Sales | `sales.v1.OrderPlaced` | (optionnel) Invoicing | Published Language | Peut etre ignore en phase initiale | | Sales | `sales.v1.OrderConfirmed` | Invoicing | Conformist | Invoicing s'aligne sur le schema upstream | | Sales | `sales.v1.OrderConfirmed` | LegacyFulfillment | ACL | Traduction vers format legacy | | Sales | `sales.v1 API` | clients externes | OHS | API stable et versionnee | ## 18. Plan d'implementation concret (ordre recommande) 1. Implementer le domaine `Sales` (aggregate `Order`, invariants, repository in-memory). 2. Ajouter use case `PlaceOrder`, puis `ConfirmOrder`. 3. Brancher `Invoicing` en appel direct naif (episode 00). 4. Introduire `contracts/sales/v1` et migrer les echanges (episode 01). 5. Formaliser `Invoicing` en Conformist avec tests de contrat (episode 02). 6. Introduire `LegacyFulfillment` ACL et isoler tout mapping legacy (episode 03). 7. Exposer `Sales` via OHS versionne (episode 04). 8. Ajouter robustesse (idempotence, outbox optionnelle, correlation-id) (episode 05). --- Ce document sert de "constitution" du repo MiniShop. Toute evolution de structure doit justifier son impact sur la context map et sur les patterns pedagogiques cibles.