Mediator Pattern
Intención y problema
¿Cómo reducir dependencias directas entre colaboradores coordinando parte de sus interacciones? Mediator concentra reglas de colaboración para que los participantes no necesiten conocer todos los demás. Pertenece a behavioral-patterns.
La intención GoF de encapsular cómo interactúa un conjunto de objetos se recoge en el material académico de CMU sobre Mediator. El diálogo de entrega usado aquí es un ejemplo propio: interesa localizar coordinación, no trasladar toda la aplicación a un objeto central.
Supongamos que seleccionar cliente cambia direcciones disponibles; elegir dirección modifica opciones de envío; elegir envío cambia el resumen y habilita Confirm. Si cada control consulta y modifica directamente todos los demás, una variación de interfaz exige revisar varias conversaciones. Un DeliveryDialogMediator puede expresar esas relaciones y dejar a cada control su responsabilidad local.
Forces y alcance de la coordinación
Queremos reducir conocimiento entre peers, pero preservar cohesión y claridad. La coordinación no desaparece: cambia dónde se describe. Si el Mediator conoce todos los internals de los participantes, puede trasladar coupling sin contenerlo.
También queremos localizar cambios de interacción sin centralizar reglas que tienen otro propietario. El componente de dirección valida su representación; las reglas de entrega del dominio conservan su autoridad. El Mediator puede pedir resultados y ordenar actualizaciones, sin convertirse en una copia de esas reglas.
La frontera debe ser concreta: coordinar un diálogo, un conjunto de componentes o una colaboración acotada. «Toda la aplicación» suele ser demasiado amplia para justificar un único contrato. Menos referencias de compilación no demuestra automáticamente menor semantic coupling.
Estructura y colaboración
CustomerSelector ─┐
AddressForm ──────┼→ DeliveryDialogMediator
ShippingOptions ─┤ ↓ coordinates participants
ConfirmButton ───┘Los colleagues/components conservan sus capacidades y colaboran mediante el Mediator. Un contrato de mediación puede expresar interacciones significativas, como customerChanged o shippingSelected. No necesita aceptar cualquier objeto y cualquier operación mediante una API universal.
En una colaboración posible:
- CustomerSelector informa una selección.
- El Mediator consulta direcciones disponibles mediante un colaborador adecuado.
- Solicita actualizar AddressForm y limpiar una opción de envío incompatible.
- Obtiene opciones válidas para la dirección seleccionada.
- Actualiza resumen y disponibilidad de Confirm.
El orden forma parte de la coordinación. Los controles no necesitan conocer toda la secuencia ni decidir qué otro control se actualiza. Sí deben conservar contratos sobre datos, resultados y señales emitidas.
Si una actualización produce otro evento, la mediación debe definir cómo evita ciclos o trabajo duplicado. Un flag genérico que ignora todas las notificaciones puede esconder cambios legítimos. Agrupar cambios o diferir una reacción son alternativas bajo una política explícita, no garantías del patrón.
Qué cambia y qué permanece acoplado
El Mediator puede localizar una regla nueva: cambiar dirección invalida también una fecha de entrega. Los participantes existentes pueden conservar conversaciones estables mientras cambia la coordinación.
A cambio, el coordinador depende de sus capacidades y de la semántica del conjunto. Si cambian sus contratos, puede necesitar modificaciones. La independencia relativa de colegas no implica independencia de todas las decisiones de negocio.
| Presión | Beneficio posible | Costo a verificar |
|---|---|---|
| Interacciones dispersas entre peers. | Un lugar para describir la colaboración. | Que el contrato no exponga todos sus internals. |
| Cambios frecuentes de coordinación. | Localizar secuencias y reglas de interacción. | Crecimiento y cohesión del Mediator. |
| Componentes reutilizables. | Retirar referencias a otros controles concretos. | Dependencia sobre señales y resultados compartidos. |
Es útil comparar un cambio real antes y después. Reemplazar llamadas directas por strings opacos puede reducir imports mientras empeora navegación y diagnóstico. El objetivo es una colaboración más modificable, no una métrica de referencias aislada.
El riesgo de God Mediator
Un Mediator que acumula business rules, workflows, persistencia, validation, authorization e infraestructura puede convertirse en un nuevo punto de alto acoplamiento. Una clase gigante que todos conocen no resuelve el problema porque sus colegas tengan pocas referencias.
Eso no prohíbe que coordine una validación o solicite autorización cuando la interacción lo exige. Exige distinguir coordinación de la implementación de cada capacidad y mantener una frontera cohesiva. Extraer funciones mecánicamente tampoco sirve si todas necesitan los mismos detalles internos.
En el diálogo, decidir cuándo refrescar opciones es coordinación; calcular todas las tarifas, administrar conexiones y duplicar restricciones del dominio serían responsabilidades adicionales que requieren otra justificación. Particionar por conjuntos de interacción puede ser más claro que crear un Mediator global.
Mediator y Observer
observer-pattern enfatiza notificación: un publisher anuncia un cambio y sus interesados reaccionan. Mediator enfatiza coordinación entre participantes: expresa cómo una interacción afecta a otros colaboradores.
Observer puede comunicar una selección al Mediator, y este puede decidir una secuencia de actualizaciones. No son mutuamente excluyentes. El registro de callbacks resuelve parte del mecanismo de comunicación; la regla «cambiar dirección invalida la fecha» necesita todavía un propietario.
En Observer, el publisher no define necesariamente todas las reacciones de suscriptores. En Mediator, las interacciones relevantes se describen en la coordinación. Ambos mantienen obligaciones de lifetime, orden, errores y reentrancy según implementación. Usar eventos internamente no vuelve distribuido al coordinador.
Facade, Service Locator y Message Bus
Facade proporciona acceso simplificado a un subsistema; no necesita reorganizar la colaboración de sus objetos internos. Mediator participa en coordinar a esos colaboradores. Una API simple y un coordinador pueden coexistir, pero atienden presiones diferentes.
Service Locator permite buscar o resolver dependencias desde un registro. Fowler desarrolla esa responsabilidad en su comparación entre inyección y Service Locator. Mediator coordina interacción. Una llamada mediator.Send(request) no demuestra que el caller esté resolviendo servicios arbitrarios, aunque una implementación pueda usar un registro internamente.
Un Message Bus puede compartir la idea de desacoplar senders de handlers. El Message Bus de Hohpe y Woolf pertenece al contexto de integración. Un bus puede introducir routing, serialization, brokers, persistence, procesos y políticas de entrega, según realización.
Mediator no equivale a Message Bus. Dispatch en proceso no proporciona automáticamente asynchronous delivery, durabilidad ni delivery-guarantees. reliable-messaging permite examinar esas decisiones sin agregarlas al patrón local.
MediatR y Vertical Slice
MediatR es una biblioteca .NET; Mediator es un patrón de diseño. Su documentación oficial describe mensajería en proceso y dispatch para requests/responses, commands, queries y notifications.
Usarla no demuestra una buena aplicación del patrón. Un handler por request tampoco constituye por sí solo una arquitectura. Hay que examinar qué responsabilidades se separan, qué dependencias se evitan y si el flujo resulta más fácil de modificar.
HTTP endpoint
↓ request dispatch
CreateOrder HandlerEl dibujo puede aparecer en vertical-slice-architecture, pero Vertical Slice no equivale a Mediator ni MediatR. Una feature puede utilizar llamada directa, dispatching o una biblioteca según necesidad. La organización del cambio y el mecanismo de invocación son decisiones distintas.
command-pattern puede representar una solicitud que el coordinador recibe o despacha. Eso no obliga a convertir todas las comunicaciones internas en Commands ni a crear una infraestructura de handlers para cada operación.
Alternativas, consecuencias y misuses
Interfaces pequeñas, funciones coordinadoras, callbacks y módulos pueden expresar mediación sin una jerarquía extensa. Reducir ceremony conserva las obligaciones de la colaboración; un diccionario de handlers puede aportar dispatch sin justificar por sí solo el diseño.
Mediator puede mejorar modificación y pruebas de secuencias acotadas. Sus costos incluyen indirection, navegación, lifecycle de participantes y un coordinador que debe conocer las interacciones. Las pruebas deberían verificar estados finales, participantes ausentes, errores parciales y ciclos relevantes, no solamente llamadas a mocks.
No ayuda cuando una relación directa pequeña ya explica el problema. Son misuses dispatch indiscriminado, dependencias ocultas, handler explosion y convertir llamadas normales en rutas opacas sin beneficio. También lo es suponer desacoplamiento conceptual porque desaparecieron referencias concretas.
pattern-selection orienta esa comparación y design-patterns conecta las familias. La revisión útil puede nombrar el conjunto coordinado, las reglas localizadas y el costo aceptado; si no puede hacerlo, la mediación merece simplificarse.