Does the mediator pattern add any value beyond dependency injection? I am encountering the mediator pattern for the first time in context of this sample application, which is meant to demonstrate how to follow Clean Architecture in .NET. I assume this pattern was included so it wouldn't be too distracting for devs who expect it to be used; but it wasn't the main focus of the sample code, or the book that this code accompanies.
In this application, we find an OrderController, which uses the mediator pattern (or at least, the MediatR library) on line 26 to retrieve data and send it back to the client. So far so good.
At this point, I'm expecting to see the receiver implemented in one of the other architectural layers. However, the receiver for this request is actually defined in the same architectural layer (the "Web" layer), leading me to question whether the mediator pattern adds any value beyond dependency injection.
Additionally, the receiver (GetMyOrdersHandler) uses bog-standard DI to access the underlying repository, which seems to put the supposedly tighter coupling in the wrong place (i.e. across the architectural layer boundary), with the loose coupling within the same layer (the UI layer).
(Side note: I have inferred from other areas in this application that the general intent is for the number of boundaries to increase as the application grows over time; however, that knowledge does not tell me why DI alone should not be used between a controller and its quintessential data.)
Furthermore, Wikipedia's description of the Mediator pattern (especially, Problems that the mediator design pattern can solve) does not clearly distinguish a use case which couldn't also be solved with DI. If two components agree to a suitable DI contract, then both of them understand exactly as much about the other as is necessary for them to communicate. What's left to abstract?
Here are the motivations for using it which I've seen so far. Many of them share a weakness in that they don't clearly explain what they mean by "interaction" or "coupling" or "peer/colleague."
It's an event loop
Although I haven't seen this mentioned explicitly, the mediator is obviously useful for processing events --- when that's exactly what you need. No-one seems to mention that, though. Also, this is obviously not the only (or perhaps even an intended) use case. Otherwise, we'd call it an event loop, and talk about algorithms instead of design patterns.
It simplifies adherence to the CQRS pattern.
Fine. But if that's the only use case, then it's not worth implementing on its own; that makes it, at most, a sub-pattern.
It allows loose coupling between "peers."
DI makes the same promise. How is this different?
It avoids DI explosion by reducing the number of dependencies that need to be injected.
This can be accomplished in other ways, like an Aggregate/Facade Service, or by breaking apart the class with the exploding constructor into mutiple classes with fewer responsibilities.
It decouples the interaction of objects from their definition.
This is hard to wrap my head around without a concrete example. What is an interaction, and why would I need to separate it out from a DI contract? How is this different from, say a service, which represents a set of interactions between a domain model and a view model --- or between the UI layer and a repository, if you like.
It allows the receiver and the sender to be compiled and run separately.
DI already provides that. All you have to do is put the contracts into a shared module which is accessible to both the sender and receiver. And, if the sender and receiver use different, incompatible programming languages, the mediator pattern doesn't save you from duplicating the contracts between them.
Sources: