DEV Community

Discussion on: Domain Driven Design avec PHP & Symfony

Collapse
 
spike31 profile image
Gilles Gauthier

C'est bien ça Ludovic ! (J'avoue que n'ai pas fais l'effort d'être clair en me relisant..!) Je parlais d'Event Applicatif.

Si je reprends ton point 4. ImageUploadedListener

Ce que je fais c'est que j'injecte le CommandBus dans le listener, pour pouvoir dispatcher la command CropPreview, et ainsi recommencer une nouvelle boucle

command -> handler -> event -> listener -> command -> handler ...

Et le tout est enveloppé, quand c'est possible, dans une transaction Mysql. Je dis quand c'est possible, car si j'ai une action tournée vers l'extérieure comme "envoyer un email", il n'y a pas de rollback/annulation possible.

C'est une mise en place que j'avais lu dans un article CQRS Journey il y a plusieurs années (rechercher sur la page "Approach 3: Using a process manager")

En suivant ce principe j'arrive à découpler mes handlers et à pouvoir "enchaîner" les commandes qui en découlent, et mon code est organisé de manière plus "lisible".

J'ai souvent vu des exemples de ce genre :

  1. Dispatch command: CreateUser
  2. Handle command: CreateUserHandler
  3. Dispatch Event: UserCreated
  4. Handle Event: UserListener
  5. Et dans le listener le code complet pour envoyer un email

Je trouve que c'est une erreur parce qu'on donne de la responsabilité à un listener, on lui donne de la logique métier, alors que (pour moi) il est juste là que pour "router" vers la prochaine command à exécuter. Comme un ProcessManager..

En tout cas j'ai hâte de lire tes prochains articles !

Thread Thread
 
ludofleury profile image
Ludovic Fleury

Super, merci pour ton retour, je vais essayer de compléter la réponse dans ce cas avec tes éléments.

Concernant les "events applicatif", du coup je suis tout à fait aligné avec ton implémentation.

c'est une erreur parce qu'on donne de la responsabilité à un listener, on lui donne de la logique métier

Ce point est très important et je soutiens l'approche. Les listeners ne doivent pas contenir de logique métier. Il y a plusieurs options pour éviter cela: soit le dispatch d'une autre commande (comme tu le fais) soit l'appel à un service applicatif autre qu'une commande (mais je trouve qu'on perd immédiatement en consistence "DX": plusieurs façon de manipuler la couche applicative).

En revanche:

Et le tout est enveloppé, quand c'est possible, dans une transaction Mysql

Là, je suis plus réservé. J'éviterai au maximum. Parce que selon les approches "tactiques" du DDD, comprendre les reco d'implémentation:

  • Les AR garantissent l'atomicité d'une opération
  • J'étends cela au niveau des commandes pour soulager le design des AR (concepts plutôt difficiles) et également éviter les SAGA

Mais, je délimite la transactionnalité d'une opération métier à 1 unique commande. Le plus difficile à maintenir dans une application, c'est surement l'intégrité des changements d'état, la diffusion de la transaction au travers de plusieurs commandes amène du risque, de la complexité de maintenance.

Comme tu peux le lire dans l'article, je fais également beaucoup de compromis ou d'économie dans mes implémentations (command handler parfois bof-bof, commande async). Et il n'y a pas de place à la pureté dans la réalité (des contextes de nos projets). Donc ce n'est pas "mauvais" et nous maitrisons nos risques dans ces décisions tech un peu "touchy", je me méfierai juste de banaliser des transactions "plus large".

Cette article est en venu spontanément en réaction à l'excellente conf de @lilobase . Je ne pensais pas rédiger plus, mais si tu as des sujets, cela peut m'inspirer?

Dernier point, dans la traduction anglaise de cet article j'ai une partie "Bonus Track" à la fin, qui parle de la synchronisation de BC's et évoque une mécanique events similaire.