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
This commit is contained in:
404
docs/MINISHOP_DDD_BLUEBOOK_BOILERPLATE.md
Normal file
404
docs/MINISHOP_DDD_BLUEBOOK_BOILERPLATE.md
Normal file
@@ -0,0 +1,404 @@
|
||||
# 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.
|
||||
Reference in New Issue
Block a user