Files
DDDBoilerplate/docs/MINISHOP_DDD_BLUEBOOK_BOILERPLATE.md
Mathias STRASSER a4a14e441b Step 00 — Squelette + intégration naïve
3 Bounded Contexts (Sales, Invoicing, LegacyFulfillment) avec :
- Domaines complets (agrégats, VOs, événements, invariants)
- Couche application (commands, queries, ports)
- Infrastructure in-memory (repos, gateway fake)
- Controllers HTTP Symfony
- Couplage naïf synchrone entre BC via NaiveSalesEventPublisher
- 20 tests unitaires et d'intégration passants
2026-03-04 00:27:15 +01:00

11 KiB

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:

Sales (Upstream)
  ├── Published Language (sales.v1.*)
  ├── OHS (query/command API)
  ├──> Invoicing (Conformist)
  └──> LegacyFulfillment (ACL)

6. Architecture de code recommandee

.
├── 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:

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)

interface SalesEventPublisher
{
    public function publishOrderConfirmed(OrderConfirmedMessage $message): void;
}

13.2 ACL vers legacy (Infrastructure)

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)

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.