Comment Ne Pas Utiliser Mediatr
Cet article est destiné à mes collègues qui utilisent MediatR comme abstraction entre les controllers web, et la couche application/domain.
Description courte de ma période élitiste dans ma carrière⌗
Comme annoncé dans d’autres articles, j’ai connu une période que j’appelle aujourd’hui élitiste. Une époque où sans comprendre les avantages et inconvénients des patterns que je consommais, j’étais convaincu que n’importe quel projet mérite le DDD, l’usage de MediatR, que le DRY doit être appliqué sans compromis. Il n’y avait qu’une seule bonne façon d’écrire du code, consommer un maximum de pattern connus dans un maximum de situations.
Ce comportement en moi m’apporté un sentiment de confiance, de conviction de bien faire les choses. Pire, à cause de la réputation qu’ont les designs patterns, mon comportement était validé par mon entourage. Ce qui me validait que j’allais dans la bonne direction. Forcer l’usage de patterns, refuser les compromis pragmatique m’a amené à avoir l’air intelligent.
Je me souviens avoir convaincu que des repositories méritait d’utiliser un Repository générique, et sur-consommer l’héritage pour les cas spécifique. C’était le summum du DRY, pour mes convictions de l’époque, ça ne pouvait qu’apporter des bénéfices. Aujourd’hui, je supplie mes collègues ne pas appliquer cette erreur si séduisante. Il est bien plus facile à maintenir et faire évoluer du code indépendant que du code qui interagit avec toutes les fonctionnalités de votre application. “Un petit copier coller est mieux qu’une petite dépendance” (Rob Pike)
Même si dans cette phase, j’ai pu produire des solutions dont je ne suis pas fier aujourd’hui, c’était une étape nécessaire pour comprendre les dégâts qu’apportaient mes convictions forte et irréfléchies. Prendre conscience de mes erreurs m’a appris à réfléchir par moi-même et avoir un sens critique de ce qui m’est présenté.
Appliquer des patterns sans réfléchir apportait une complexité inutile, retardait les projets, augmentait les couts de maintenance pour au final, juste compenser mon désir de bien faire les choses. Aujourd’hui, je rédige cet article en espérant donner des pistes de réflexions à tous ceux qui passent aussi par cette étape. Mes habitudes étaient pleines de faux raisonnement, j’ai appliqué beaucoup de design erroné. Explorons l’une d’elle, l’usage systématique de MediatR.
Pour faciliter la lecture de la suite de l’article, appelons le moi du passé Nicolas à la 3e personne.
Je ne suis pas certain pourquoi, mais mon entourage m’appelle parfois Nicolas ou Thomas. Sûrement à cause de mon nom Scolas.
Mon raisonnement passé avec MediatR.⌗
Nicolas veut être professionnel. Cela signifie pour lui produire du code élégant. Il n’entend pas l’importance du retour sur investissement, de faire de la cohésion entre les classes, entendre les désavantages. Comme il lui a été conseillé que MediatR permet de découpler les classes, et que le découplage était perçu comme élégant, il était facile pour Nicolas de présumer que MediatR est la solution à appliquer partout. Une solution qui abstrait tous les systèmes les uns des autres. Il était simple pour Nicolas de justifier que MediatR apporte de l’abstraction qu’un bon projet mérite. Une justification acceptée par son entourage. En plus d’avoir une certitude sans restriction, il y avait aussi une validation de ses pairs.
Spoiler alerte, J’employait MediatR comme une abstraction au lieu de simples interfaces. De ma mémoire, je n’ai jamais employé MediatR pour employer un pattern de mediator, celui de faciliter la coordination entre plusieurs composants. Aujourd’hui, j’ai pu reconnaitre l’usage du pattern mediator dans un autre contexte : celui de smart component dans les frontends, comme décrit ici https://www.jetbrains.com/guide/javascript/tutorials/react_typescript_tdd/presentation_components/. À force d’user MediatR pour des mauvaises raisons, j’étais incapable de reconnaitre le pattern mediator alors que je le recommendais sous un autre nom. C’est avec un peu de honte (et un peu de fierté d’avoir dépassé cette étape) que j’avoue n’avoir jamais utiliser MediatR pour les bonnes raisons.
En détail un bon et mauvais usage⌗
Les années passent, Nicolas découvre la raison d’être de MediatR : dans l’objectif de ne pas porter une intelligence partagée entre plusieurs composants, une intelligence centrale peut diriger les états de sous-composants. Un exemple naïf peut-être retrouvé ici, grandement inspiré des implémentations communément admises en React.
À l’opposé, Nicolas avait pour habitude de découpler les controllers de la couche Application via MediatR.
Comparons l’arbre d’appel entre les 2 choix.
flowchart ControllerM(Controller) --> MediatRM(MediatR Application Handler) MediatRM --> DomainM(Domain) MediatRM --> InfrastructureM(Infrastructure) DomainM --> InfrastructureM Controller(Controller) --> Application Application --> Domain(Domain) Application --> Infrastructure(Infrastructure) Domain --> Infrastructure
Je ne suis pas le seul à utiliser erronément le pattern mediator. Même les 2 premiers avantages décrits par chatgpt décrivent exactement son usage que j’estime inutile https://chatgpt.com/share/7d9cf933-a92c-47f3-854f-c754db3230e0. Une recherche google avec “why to use mediatr” donne des résultats mixés. Entre les bons usages écrits plus bas, il y a surtout une bonne partie de “découple” entre les couches. Est-ce que votre code aurait une organisation différente si votre objectif est de découpler les controllers de la couche application ?
De toute évidence, ce cas d’utilisation de MediatR n’apporte pas d’avantage. Alors que le pattern Mediator est spécifié dans l’idée de réagir à des événements, la majorité des cas que Nicolas a implémentés est un usage de commande. Autrement dit, les controllers envoient un objet à MediatR dans l’intention d’apporter une instruction impérative au système. Comme dit dans “Design patterns: elements of reusable object-oriented software” (1995), “A mediator is responsible for controlling and coordinating the interations of a group of objects […] The objects only know the mediator.”. Il n’est pas question de faire abstraction entre 2 couches, mais bien de coordonner des objets entre eux.
MediatR apporter un découplage, vrai ?⌗
Oui, MediatR apporte un découplage, mais elle n’en apporte pas plus qu’une interface. Si vous désirez abstraire l’usage d’une classe concrète, alors une interface est suffisante.
Un paragraphe pour dire 2 phrases ? Oui, c’est aussi simple que ça. Il n’y a pas de matière à débat.
Quels problèmes MediatR résout ?⌗
Les pipelines de MediatR peuvent apporter un avantage. Si je n’ai jamais rencontré dans ma vie l’opportunité de l’utiliser, que je préfère apporter le logging et la validation directement dans mes classes applicatives, je reconnais que centraliser cet usage peut être utile. Utiliser des adaptateurs si jamais il y a un avantage à appliquer une logique avant ou après l’exécution d’une méthode m’a toujours suffi.
Une autre situation où MediatR est idéale est dans l’usage des domain events. Si vous avez un domaine complexe et dont le DDD est réellement le bon choix, je vous recommande l’usage de MediatR. Même si je n’ai pas d’expérience personnelle à donner à ce sujet, je suis aujourd’hui convaincu que MediatR est le bon choix. De plus, coordonner des sous-domaines ressemble bien plus à l’intention initiale du pattern Mediator.
Résumé⌗
Par le passé, l’usage de MediatR a rempli mon désir de produire du code découplé. Mais toutefois, son usage n’était qu’illusoire. MediatR m’a permis de me donner le sentiment d’apporter une plus-value à mes projets. Aujourd’hui, je recommande de ne plus l’utiliser à l’exception des domains events. Pour découpler les différentes couches d’une application, une interface suffit. Pour appliquer le pattern mediator, une implémentation sans librairie est ce qu’il y a de plus efficace.
S’il vous plait, n’acceptez pas les implémentations au nom de l’élégance. Demandez-vous si les solutions que vous apportez correspondent aux arguments que vous leur associez. N’acceptez pas les recommandations d’autrui aveuglément. Prenez le temps de réfléchir et d’expérimenter.
EDIT: 02/08/2024, mise à jour d’un lien mort
EDIT: 03/04/2025, correction orthographique, meilleure comparaison avec le mediator décrits dans Design Patterns, phrasing.
A propos de l'auteur
Je m'appelle Mathieu Scolas. Constatant que peu de blogueurs proposent du contenu personnel, constructif et nouveau, ce blog tente de compléter ce manque. Il me permet de partager des guidelines et exprimer des opinions sur le développement d'application. Dans la volonté d'éveiller les lecteurs sur différents sujets, ma façon est de comparer les bonnes pratiques avec les interprétations populaires, ou de présenter des sujets peu évoqués ailleurs. Ce blog me sert aussi de source pour exprimer au mieux des sujets qui méritent un support pour expliquer convenablement les avantages des pratiques, comment les appliquer correctement, et quelles en sont les origines. Retrouvez-moi sur Linkedin et Github.