![](https://habrastorage.org/getpro/habr/upload_files/d2b/7a4/35a/d2b7a435ad147306cc24859f5503d78f.png)
Этой весной в чатах и сообществах dotnet (не забываем #DropTheDot) обострились анти-медиаторные настроения. Поначалу меня это забавляло, потом удивляло: люди подхватывают лозунги, не пытаясь разобраться в вопросе. Квинтэссенцией обострения стал доклад Андрея Парамонова "MediatR не нужен”, шокирующий своей категоричностью аргументов. В докладе были зерна истины, но они к концу так и не проросли.
Желание прорастить эти зерна и мой крайне положительный опыт использования медиатора в проектах разного размера смотивировали меня написать данную статью. Статья основана на личном опыте и наполнена эмоциями, за это заранее извиняюсь ? Она будет интересна скорее людям, которые уже знакомы с медиатором, встречались с ним в теории или на практике.
С чего медиатор начался и где он сейчас
В дискуссиях о медиаторе часто возникает недопонимание и путаница в терминах. Чтобы этого не случилось при чтении данной статьи, я добавлю небольшое введение о терминах, о которых мы сегодня будем говорить. Существует целых три смежных явления, которые называют медиатором, и о которых нужно было бы говорить отдельно, но, к сожалению, они имеют одинаковые названия, поэтому мы страдаем, а злопыхатели этой неопределенностью пользуются. Проясним эту неопределенность и проведем краткий экскурс. Можете пропустить эту часть, если уверены, что понимаете, чем отличаются подзаголовки ниже.
Медиатор как design pattern
Медиатор - шаблон проектирования, design pattern, про который можно прочитать в разных книжках, или, например, вот здесь. Выглядит он примерно вот так:
![Медиатор как design pattern Медиатор как design pattern](https://habrastorage.org/getpro/habr/upload_files/13a/b63/976/13ab6397656dcfa1fc8bcd3cb525bc27.png)
Согласитесь, мало похоже на то, что мы видим в обсуждениях и страстях вокруг медиатора, но этот шаблон вдохновил появление известной и обсуждаемой, той самой, библиотеки. Тут нет никаких Behavior-ов, Request-ов, Notification-ов, нет даже команд и запросов. Суть шаблона в разрыве зависимостей между объектами введением медиатора как дополнительного связующего звена. Медиатор занимается построением общения между объектами, которые раньше общались друг с другом напрямую.
The library
Библиотека за авторством Jimmy (автора другой скандальной либы AutoMapper). Библиотека очень простая: написать ее вы сможете буквально за день, а за два дополнить тестами. Это если знать, что писать, и выкинуть новые фичи, вроде поддержки AsyncEnumerable. Чем же эта библиотека отличается от шаблона выше? Тем, что это реализация шаблона определенным конкретным образом, с новыми введенными абстракциями, такими как Request, Handler и Behavior. Здесь впервые появляются определенные тенденции организации кода под эту библиотеку. Теперь любой класс-сервис* можно представить в виде комбинации Request-ов и их Handler-ов. Каждый метод становится Handler-ом, а его сигнатура превращается в объект-запрос Request.
*такой класс, который не имеет состояния, а скорее является просто сгруппированным набором функций
Такой подход привлек множество энтузиастов, и с различными применениями этого подхода они упражняются до сих пор. Но со временем медиатор нашел свой локальный максимум полезности и начал постепенно захватывать нишу применения себя определенным образом. Этот определенный образ мы разберем в следующем разделе, а пока позвольте упомянуть эпохальный доклад Максима Аршинова — Быстрорастворимое проектирование. Именно с него началась популярность медиатора как общепринятого подхода по организации кода. Кстати, очень забавно сейчас его пересматривать, ведь многие пункты, которые упомянуты в этом докладе как плюсы, в докладе Андрея из начала статьи приводятся как минусы ?
Архитектурный стиль в контексте чистой архитектуры
Итак, медиатор как библиотека, которая вдохновилась медиатором как шаблоном, синтезировала руками сообщества определенный способ организации кода, или архитектурный стиль. Именно этот способ и стиль мы сейчас чаще всего называем медиатор. Именно его хвалят и ругают, именно к нему обращены все претензии. В дальнейшем мы не будем говорить про конкретную библиотеку (можно реализовать ее самостоятельно, исправив названия типов и другие субъективные моменты) или шаблон (который не более чем вдохновитель). У самой библиотеки, кстати, есть уже альтернативы, одна из них написана на source generators. Но это ничего не меняет, мы в дальнейшем будем говорить именно про архитектурный стиль.
Давайте рассмотрим стиль поподробнее, определимся с его местом и ролью в проектах разных типов и разных сложностей. И начнем с самого сложного варианта, со схемы чистой архитектуры в выдуманном примере домена книг и библиотеки.
![Упрощенная схема организации кода на основе чистой архитектуры Упрощенная схема организации кода на основе чистой архитектуры](https://habrastorage.org/getpro/habr/upload_files/0fd/d43/3c7/0fdd433c78189192ab50e49ca01855da.png)
Application + Domain это ядро нашего приложения. UI (слева) это точка доступа в наше приложение, и Infrastructure (справа) это внешние зависимости, к которым мы иногда обращаемся. Мы не хотим засорять ядро приложения, поэтому финтом под названием инверсия зависимостей мы вынесли инфраструктурные зависимости в отдельный слой. Вызывать их можно посредством интерфейсов, которые являются частью ядра приложения, реализации же этих интерфейсов лежат в инфраструктурном слое. На входе сразу после UI у нас идут сервисы с набором методов, часть из которых отдает данные, а часть их меняет.
Так где же место медиатора во всей этой схеме? Опытом наших предков выяснилось, что удачнее всего он встраивается на границе между UI и Application. Давайте посмотрим на вариант с медиатором.
![Упрощенная схема организации кода на основе чистой архитектуры с использованием медиатора Упрощенная схема организации кода на основе чистой архитектуры с использованием медиатора](https://habrastorage.org/getpro/habr/upload_files/ec3/18e/d11/ec318ed112e596b629fdfd315b59c664.png)
С медиатором интерфейс ядра нашего приложения для UI слоя представляет из себя набор кверей и команд. Команды и квери обрабатывают хендлеры, каждый из которых представляет отдельный юзкейз. Никаких сервисов, интерфейсов на этом уровне теперь нет. Единственный способ UI слою взаимодействовать с ядром нашего приложения это кидать запросы или команды. Сам по себе медиатор как библиотека тут выполняет чисто инфраструктурную незаметную роль. Важными являются именно объекты кверей и команд. В теории, мы бы могли совсем избавиться от медиатора, внедрять хендлеры как зависимости во все места, где мы кидаем команду, и вызывать на этом хендлере метод. Подход бы не поменялся, но код стал бы шумнее, с этим шумом и помогает справиться медиатор.
Хочу еще раз обратить внимание, что именно набор кверей и команд является точкой доступа к нашему приложению, а не интерфейс медиатора с методом Send, как может показаться. Медиатор это просто способ вызвать эти квери и команды. Построение интерфейса ядра приложения через квери и команды и есть основа описываемого архитектурного стиля.
Второй важный момент. Нет никакого медиатора между ядром и инфраструктурой. Нет медиатора внутри ядра. Медиатору отведено единственное четкое место, где он располагается, это интерфейс ядра приложения. Способ взаимодействия с внешним миром.
Есть одно исключение — еще одна, отдельная, возможность медиатора, которая называется Notifications, и которая представляет собой асинхронную событийную модель. В отличии от стандартных c# событий такая модель работает на поиске по типам, а не на явных подписках или отписках. Давайте рассмотрим, где это может пригодиться. Если вы разрабатываете код в православном DDD стиле, то знаете, что единицей изменения там является агрегат. Агрегаты для общения между собой используют доменные события. Вот тут-то на помощь и приходит медиатор с его моделью публикаций событий (или нотификаций в терминах библиотеки). Мы собираем события в рамках юзкейса, а потом публикуем их, используя медиатор. На эти события мы подписываемся хендлерами, которые выглядят 1 в 1 как хендлеры команд и являются полноценными членами слоя приложения. Они реализуют юзкейсы, которые вызываются по событиям от других агрегатов. Звучит без примера запутанно, но на реальном коде получается органично и красиво.
Архитектурный стиль медиатор, но не чистая архитектура?
Роль, которую медиатор выполняет в чистой архитектуре, на самом деле может быть исполнена в любой архитектуре. Ниже примеры схем абстракций сервиса, показывающие место медиатора в трехслойной и в двухслойной архитектурах.
![N-слойная архитектура с медиатором N-слойная архитектура с медиатором](https://habrastorage.org/getpro/habr/upload_files/fc4/1f3/e73/fc41f3e7371f0ee2b738445bfa648625.png)
Как видим, и здесь хендлеры занимают место классов-сервисов на границе с UI. UI с приложением продолжает общаться исключительно с помощью запросов и команд. Все преимущества и проблемы, описанные в дальнейшем в статье, применимы и к этим архитектурам. Если у вас нет сложного бизнес домена, нет желания выстраивать чистую структуру сервиса или аллергия на DDD, вы все равно можете использовать медиатор и получать все его плюшки. Главное выделить четкое место для медиатора и придерживаться своих правил.
Плюшки и зачем мы ими балуемся
У такого подхода и архитектурного стиля есть множество преимуществ. Какие-то из этих преимуществ доступны благодаря конкретной библиотеке, какие-то из-за самого подхода, а какие-то достаются в подарок. Начнем с основного. Почему в принципе такой подход может давать нам преимущества.
Действия — это объекты
В традиционном подходе с сервисами, которые вызываются контроллерами, каждое бизнес действие обычно представляет из себя отдельный метод в этом сервисе. Метод описывается сигнатурой, именем, аргументами, возвращаемым значением.
public class IOrdersService
{
Task<Guid> Create(
Guid? CustomerId,
IReadOnlyCollection<Guid> ProductIds,
string Address,
string? DeliveryComment);
}
В подходе с медиатором бизнес действие становится объектом. У нас все так же есть название, это имя класса. Аргументы становятся свойствами в этом классе, а возвщаемое значение определено через специальный интерфейс.
public record CreateOrderCommand(
Guid? CustomerId,
IReadOnlyCollection<Guid> ProductIds,
string Address,
string? DeliveryComment) : IRequest<Guid>;
Теперь, чтобы вызвать действие создания объекта, нам не нужно вызывать метод, вместо этого мы создаем экземпляр описанной команды и кидаем ее в медиатор.
В некоторых ситуациях, это может существенно облегчить нам жизнь. Экземпляры классов с состоянием легко сериализовать. Например, записывать в лог каждое действие вашего приложения вместе со всеми ее аргументами становится просто. С сериализацией проще изучать и отслеживать код, проще откладывать операции, например, через шедулер, проще реализовать аудит произведенных действий. Так же мы получаем четкое имя действия, в сервисе название метода всегда надо учитывать в контексте класса, команда же существует самостоятельно и может иметь полноценное понятное имя. При этом нам ничего не мешает добавить к командам неймспейс (=разложить по папкам), если мы хотим группировать их каким-либо образом. Отдельный тип для команды так же позволяет нам легко помечать ее с помощью атрибута (это пригодится нам позже).
Универсальный пайплайн
Второе важное преимущество такого подхода с медиатором это пайплайн обработки запросов. Медиатор мы разместили на входе в ядро приложения, следовательно можем гарантировать, что любое взаимодействие с нашим приложением начинается и проходит через пайплайн обработки запросов в медиатор.
Преимущества пайплайна вам наверняка знакомы и вы ими наверняка пользовались, разрабатывая различные middleware для aspnet приложений. Но медиатор позволяет пойти дальше, и не зависеть от абстракций, которые относятся к UI слою (от aspnet фреймворка в данном случае). Теперь у вас есть свой полноценный пайплайн на уровне приложения. Он будет использован и при вызове из контроллера, и из произвольного BackgroundService, и из задачи по расписанию, и из обработчика сообщения с очереди или стрима. Таким пайплайном можно пользоваться абстрагировавшись от UI слоя. Давайте посмотрим, как этот пайплайн мы можем использовать.
Поведения — бесплатное аспектное программирование
Совмещая два преимущества выше, мы можем констатировать появление в нашем приложении абсолютно бесплатного аспектного программирования. Без инъекций кода, без рекомпиляций и без страданий. Реализация аспектов в библиотеке медиатор называется behavior (далее поведения). Работают они похожим образом на middleware aspnet-а — вызываются друг за другом в порядке регистрации (своеобразный chain of responsibilities). Про такой взгляд на медиатор рассказывал Денис Цветцих в своем докладе Аспектно-ориентированное программирование на C# и .NET вчера, сегодня и завтра. Там разбираются разные подходы, но упоминается и медиатор.
Напоминаю, что в докладе из начала статьи Андрея Парамонова большую часть времени докладчик разбирает разные поведения и говорит как они плохи и неявны. И альтернатива, которую он предлагает, это реализовывать каждый аспект в каждом отдельном действии.
Мой ответ на это очень прост, это невозможно. Невозможно написать одинаковый код в тысячах различных бизнес действий приложения, ничего не забыть и не ошибиться. Невозможно потом исправить или дополнить какой-то аспект, когда он у тебя скопирован в тысяче мест. Невозможно в какой-то момент добавить новый аспект, и пройтись в разумные сроки по всем этим действиям и как-то это потом протестировать. Уж точно это невозможно, если это микросервисный продукт, над которым работают множество разных команд. На счет тысячи, я не преувеличению. К примеру, на моем текущем проекте я посчитал количество команд и кверей, и оно достигает 1300 штук. Наверняка есть проекты, на которых действий больше на порядок. Напоминаю, что без медиатора, это были бы обычные методы, разбросанные по разным частям приложения, разным сервисам, описанные произвольным образом, с неговорящими именами (без контекста класса, в котором они находятся). Попробуйте теперь для этого всего внедрить единый template для структурного логирования.
Поведения медиатора, вынесенные в отдельные либы, позволяют добиваться поразительного единообразия и легкости в управлении аспектами. Написанные один раз и протестированные, инфраструктурные аспекты позволяют разработчикам подключить себя как встроенную функцию и сосредоточиться на написании бизнес-логики. Множество инфраструктурных аспектов при этом требуют как раз единообразия. Например, метрики, которые должны выглядеть одинаково и “сливаться”, не зависимо от того, в каком сервисе они находятся.
Другой обозначенный в докладе минус, а именно неявность и сложность отладки, безусловно присутствует и поднимает порог вхождения в кодовую базу. Но это лечится документацией и качественно поставленным onborading-ом. В каждом проекте есть свои особенности, которые обязательно нужно узнать до того, как приступать к разработке на этом проекте. Я бы медиатор включил в эти особенности и закладывал полдня для каждого нового разработчика, которые бы он потратил и понял:
что любые транзакции в проекте всегда создаются в поведении,
что все логирование реализовано в поведении,
что бизнес метрики измеряют время бизнес-операции и не учитывают сериализацию,
что порядок подключения поведений важен и не нужно его менять (можно рядом написать предпочитаемый порядок их подключения)
решив таким образом 90% проблем, описанных в докладе. В крайнем случае, на тот же порядок подключения можно написать тесты. К сожалению, вместо решения проблем, автор доклада предлагает писать одно и то же в тысяче мест, а потом, видимо, редактировать это через массовый replace текста регулярками. Это безусловно будет явно, но…
Давайте вернемся к аспектам и их возможностям:
Упомянутый уже единый формат метрик. Представим, что вы разрабатываете микросервисый проект вот уже пять лет, и недавно заметили такую классную штуку как OpenTelemetry. Вам сразу захотелось добавить метрик. К счастью, вы с самого начала приняли решение использовать во всех сервисах медиатор, и теперь все что вам нужно сделать, это добавить поведение, которое будет измерять время выполнения операции и пушить метрику. Максимум полдня с тестами и у вас покрыт метриками проект любого масштаба. Будут ли какие-то пробелы и захочется ли вам где-то метрик более точных и крутых? Да, именно там вы их и доработаете, а весь остальной код у вас будет покрыт метриками за полдня.
Логирование — это всегда палка о двух концах, и в идеале бы хотелось, чтобы когда все хорошо, то логи не писались и не влияли на производительность, а когда все плохо начинали писаться. В разных библиотеках логеров есть способы фильтровать логи по неймспейсам и динамически их включать и выключать. Что еще может дать нам медиатор? Во-первых, никакой неймспейс не позволит нам разделить все действия на команды и квери, а с медиатором мы можем это сделать. Это значит, что мы можем включать логирование только для команд (более редких, но более значимых в большинстве случаев). Кроме того, т.к. команда это объект и ее легко сериализовать, мы можем без рекомпиляции включать и выключать ее логирование с аргументами, т.е. у нас есть не только действие, но и параметры. Напоминаю, что это все доступно для всех действий во всех ваших проектах (если вы, конечно, использовали там медиатор). Более того, достаточно легко написать код, чуть жирнее, строчек на 150, чтобы иметь возможность в конфиге указывать: логировать только команды, логировать только команды с префиксом Create, логировать только команды из перечисленного списка, логировать только команды, которые по времени выполняются дольше, чем 100мс. Условия ограничены только вашей фантазией.
Аудит. Очень похоже на логирование, т.к. мы можем сериализовать все вносимые изменения, то аудит удобно построить на поведении. Да, тут нужно немного подумать и понять, как доставать контекст, например, исполнителя, ip адрес вызова. У нас сейчас это сделано распространением header-ов, как в http, так и в брокере сообщений. Но можно придумать и что-то интереснее. Самое главное, что аудит будет написан в 1 месте, никаким образом не будет засорять вашу бизнес логику.
Транзакции. Более узкий и более спорный вопрос. На некоторых проектах это излишне. Транзакций может не быть в принципе, если у вас, например, MongoDB. Или вы можете захотеть создать более явный UnitOfWork и его ворочить. Но в общем случае с медиатором у вас всегда есть хорошая альтернатива — это реализовать транзакции в рамках сервиса через TransactionScope. EfСore, Dapper, linq2db начнут сразу же с этим работать. Возможно, будут проблемы с nosql базами и их драйверами, если вам вдруг нужны транзакции там, нужно будет придумывать что-то свое. Но это реализуемо и не так сложно. Основное преимущество выноса транзакций в том, что мы ими не мусорим в коде, т.к. это инфраструктурный аспект.
Это только малая часть проблем, которые можно решить медиатором. Я перечислил наиболее общие, которые могут пригодиться всем. В процессе разработки у вас наверняка наберется несколько специфичных для вашего продукта и вы будете рады, что у вас есть такая удобная точка расширения. Давайте теперь обсудим проблемы, которые несет с собой медиатор, его цену.
Реальные проблемы
Начнем с плохих аспектов, которые были упомянуты в докладе из начала статьи, и с оценкой которых я согласен.
Авторегистрация
Не стоит автоматически через рефлексию регистрировать поведения. Каждой кодовой базе, приложению, сервису, может быть нужен свой набор аспектов в своем порядке. Авторегистрация может приводить к проблемам. Не делайте так.
Валидация
Если мы вернемся к картинкам с архитектурами, то увидим, что медиатор обычно находится между UI и Application. Он заменяет собой слой сервисов, где могли бы располагаться юзкейсы. В юзкейсах нет никакой общей валидации. Подразумевается, что нам пришли валидные данные с UI с точки зрения системы типов. Т.е. енамы корректно заполнены, нет строк из пробелов, авторизацию мы всю уже прошли. Я просто не вижу, что на этом этапе можно валидировать. Техническая валидация значений уже должна быть проведена. Бизнес валидация, с бизнес-правилами происходит в домене. И что ей делать в каком-то поведении, при чем частично (т.к. каких-то данных точно хватать не будет) не очень понятно. Поэтому не рекомендую делать валидации в поведениях, там этому не место. Если вдруг вы решили в UI слое не делать никаких DTO-х, а принимать команды и квери прямо в контроллеры, то вы можете захотеть валидировать эти команды и квери, но поведение все еще неправильное место. Используйте для этого FluentValidator, так же как и с обычными DTO.
Автоматическая публикация всех доменных событий
Если честно, я такое (в отличии от первых двух пунктов) в реальной жизни ни разу не видел, но если оно существует, то лучше не стоит так делать. Стоит сделать отдельный контракт публичных событий, и публиковать их всегда явно руками. Да, вы можете использовать Notifications медиатора для публикации доменных событий. Но публиковать их, например, в RabbitMQ, нужно явно. Подпишитесь на доменное событие и опубликуйте ваше публичное, из контракта. Тут же вы можете и команду вызвать как реакцию на доменное событие, но это уже другая история.
Распределенная блокировка
В аспекты и поведения стоит выносить вещи, которые либо будет круто и полезно иметь по всему коду (логи, метрики), либо будет круто иметь возможность ситуативно включать в любой части кода, при этом придерживаясь единообразия (транзакции). Ни к тому, ни к другому распределенная блокировка не относится. Это в принципе плохо, и лучше ее не использовать, но если уж реально пришлось, то такой костыль нужно изолировать и не давать возможность такому распространяться по кодовой базе.
Data enrichment и control flow
Модификация передаваемых реквестов — это плохой подход, действительно вносит много путаницы и мешает использовать иммутабельные типы. Для описанной задачи, возможно, подойдет хранение контекстных данных в ScopedService или в AsyncLocal переменной. Лучше сервис, но все зависит от конкретной задачи.
Правило хорошего тона — это писать код поведений таким образом, чтобы команды и квери выполнялись корректно и без поведений вообще. А исключения и костыли реализовывать другими средствами, не поведениями.
Retry
Тут история такая же как и с валидацией, мы используем медиатор в качестве доступа к нашему слою приложения, и это не его ответственность заниматься ретраить. У него есть некая операция, он ее пытается выполнить, и если не получается кидает ошибку (в общем случае). Retry может существовать, но это история UI слоя, это может сделать consumer при обработке сигнала из очереди (он может и redelivery сделать), это можно сделать в настройках шедулера джоб (hangfire, quartz), или же в рамках aspnet отдать это на откуп клиента, который опционально может так же ретрайнуть.
Навигация
С ростом количества кверей и команд, особенно если вы их часто переиспользуете, вызываете из множества мест, находить обработчик становится неприятно. Тут я согласен, но подчеркну именно слово неприятно, да, это тормозит вас на пару секунд и может мешать воспринимать целостно цепочки вызовов различных частей приложения, но это не смертельно. С этим стоит бороться, я бы хотел увидеть расширение или скрипт, которые бы автоматически перебрасывали нас в хендлер. Второй вариант, это располагать обработчик прямо под кверей или командой в том же файле. Это не везде возможно: например, кверя может быть объявлена в слое приложения, а ее хендлер в инфраструктурном слое рядом с репозиториями. Но там, где это возможно, это хороший вариант.
Только, прошу вас, коллеги-любители медиатора, давайте не заводить по папке на каждую кверю, это и правда выглядит так себе ? Либо кверя и под ней хендлер, либо отдельный файл в вертикальном слайсе под все квери и команды. Сейчас с существованием рекордов нет никакого смысла создавать отдельные файлы для 3-5 строчных типов.
Лично я использую либо подход с одним файлом, либо, выделяя название квери, нажимаю CTRL+T
. Поиск решарпера и райдера всегда ставят хендлер на второе место, он умный.
Отсутствие явных зависимостей
Еще один частый аргумент против подхода с медиатором — это отсутствие явных зависимостей в контроллере (или месте откуда вы отправляете команды и квери). Мол получив ссылку на IMediator
мы можем творить что угодно, и вообще это сервис локатор и антипаттерн.
Но так ли эти зависимости нам помогают? Условный сервис в сложном проекте, это мешанина из огромного числа не связанных между собой действий, которые частенько вызывают друг друга. И получив зависимость на условный IUsersService
никаких гарантий вам это не дает, да и понимания тоже. Я согласен, что, если забыть про гарантии и говорить в терминах удобства, действительно удобно понимать действия из какого вертикального среза, из какой части приложения мы вызываем в данном контроллере.
Давайте подумаем, можем ли мы это удобство перетащить на проект с медиатором. На самом деле можем, только нужно привыкнуть смотреть глазами чуть выше ?. Обычно сервис в традиционном проекте — это какой-то вертикальный слайс, который заключает в себя действия с одним агрегатом или с семейством агрегатов. Если мы заменим сервис на набор команд и кверей, вертикальный слайс никуда не денется, мы точно так же можем оставить эти квери и команды в папке слайса. А папка это неймспейс. Следовательно, условный LibraryService.UsersSlice.Commands
в юзингах это и есть аналог инъекции IUsersService
в контроллер. Да, чуть менее типизировано, но если у кого-то есть желание контролировать отсутствие вызовов из контроллера команд и кверей из чужого слайса, то здесь это будет видно. На пул реквестах в том числе.
По поводу сервис локатора и других странных претензий. Хочется замемить и сказать это другое. Медиатор дает вам возможность запросить или запустить одно из действий, которые вы явно ему для этого объявили и расшарили из ядра приложения. Он не имеет возможность дернуть произвольный метод любой зависимости вашего проекта, которая лежит в контейнере (как это бывает с локатором). Скорее всего, люди имеют ввиду то же самое отсутствие явных зависимостей, которое мы уже обсудили, но если нет, то Jimmy Bogart уже отвечал на такие вопросы, предлагаю прочитать статью по ссылке, второй пункт (но лучше всю).
Заключение
Подводя итог, я бы сказал, что медиатор — это инструмент со своими ограничениями и с правилами использования, он не всемогущ и не всесилен. Желательно знать ограничения перед тем, как его использовать. Если же вы добавили медиатор в проект, не нужно решать каждую инфраструктурную проблему с помощью поведения. Так вы избежите множества граблей. Читайте и смотрите на чужой опыт, экспериментируйте и щупайте. На моем опыте медиатор мне приносил гораздо больше пользы, чем проблем. Я к нему испытываю юношеские теплые чувства ?
Если серьезно, то многие инфраструктурные задачи, которые обычно ухудшают код и его читаемость, медиатором решаются элегантно. Эта статья — резюме моего опыта написания приложений разных масштабов с использованием такого подхода. Своеобразный ответ на новый “тренд” хейта медиатора. Надеюсь, мне удалось показать его сущность, обозначить область применимости и подсветить положительные стороны. Теперь вам есть чем обосновать добавление медиатора на свой проект!
Так как эта статья была ответной реакцией, она получилась не слишком полезной в плане практики, подразумевалось, что люди уже используют медиатор, или, по крайней мере, его видели, но какое-то мнение по нему не сформировали. Если вам статья зайдет, я попробую написать более практическую вторую часть, с рабочими примерами и репой, где мы попробуем на практике рассмотреть применимость и ограничения медиатора (а возможно это будет какая-то более общая архитектурная тема).
А пока что удачи, используйте медиатор, программируйте на дотнете. Дотнет это хорошо!