2016-12-16
Mes notes brutes sur le BBL "Domain Driven Architecture" du 16 décembre 2016, par @lilobase chez @novencia.
-
Même language = paramount (le top)
-
Ne jamais contraindre le métier
-
Framework fullstack = poubelle (car le métier se retrouve éparpillé dans des répertoires imposés par le framework et mélangé avec la couche infra et applicative)
-
CRUD = pire saloperie (car on spécifie la persistance plutôt que le métier, et on n'utilise plus les verbes du métier)
-
Archi Dossier <- celle du métier
-
Héritage = uniquement spécialisation d'un type, tout le reste composition ou agrégation.
Agrégats DDD : durée de vie liée, ligne de facture dans facture.
Ex de durée de vie pas liée : client dans facture
Use case qui modifie : command DTO Use case qui ne modifie pas : query DTO
Command bus : lier des commandes à un handler (qui orchestre les transactions métier, transaction script)
Des middlewares dans le bus, comme validation de type, mais jamais validation métier
Toujours parler à la racine d'un agrégat, pas de raccourci.
Contexte A : un Catalogue contient des Produits qui sont des Entités.
Contexte B : Invoice qui contient des InvoiceLine et des Produits. Dans ce contexte, le produit n'est plus une Entité, il devient un ValueObject. Deux concepts différents qui ont le même nom mais pas le même sens en fonction du contexte.
Bounded contexts hermétiques. Sortir les concepts transverses, les mettre dans SharedKernel. Attention, c'est probablement une énorme connerie donc se poser la question plusieurs fois avant de le faire. Jamais les Utilisateurs dans shared. Context Authent & Identité. Pour l'Invoice, des Acheteurs.
EventSourcing très compliqué, éviter.
Un répertoire "model", avec un rep "invoice" (souvent le nom du rep est le nom de l'agrégat)
C'est l'agrégat root qui est responsable de toutes les validations. Une fois instancié, on sait qu'il est valide.
Une interface InvoiceRepository, avec en général trois méthodes : save() (i.e. upsert), exists(), findById(). Note : on est toujours dans la partie Commands. Ne jamais confier l'ID (sa génération) de l'agrégat à la BDD. ID métier (plaque d'immatriculation) ou si possible pouvoir le générer de n'importe-où (hash du contenu).
Une table avec une colonne ID, une colonne contenant l'objet serialisé, et éventuellement quelques colonnes pour les recherches (dates).
Un répertoire "infrastructure", avec un répertoire "invoice", une classe InvoiceSQLRepository chargé de serialiser. Un builder.
Le catalogue expose un service, qui passe une serialisation d'un produit, qui à travers une translation map donne un produit côté invoice.
Queries -> querybus
InvoicesSQLQueries InvoicesReddisQueries
Pas les mêmes datastores que les commandes. Au save de l'agrégat, un événement est propagé pour mettre à jour les projections. Les projections peuvent être reconstruites from scratch.
BDD graph: objets très légers. Juste des id et des relations, les ID pointent sur du reddis qui dépote.
Mongo : bien pour des données arborescentes (pas de relationnel pour ne pas sortir du shard).
Du JSON entre bounded contextes. Ensuite si besoin, HTTP.
Anti corruption layer.
Au dessus du CQRS : un proxy http en js.
Gestion d'erreurs
La commande qui échoue envoie un évent dans le bus de statut.
Note : les différents bus ne sont ni dans le contexte ni dans le proxy.
Un endpoint "xxx/commands" pour les contextes.