Две главные ошибки при использовании MediatR

Моя цель - предложение широкого ассортимента товаров и услуг на постоянно высоком качестве обслуживания по самым выгодным ценам.

MediatR является, безусловно, очень успешным и популярным open-source проектом, который глубоко проник в недры коммерческой разработки. Однако, сколь популярен инструмент столь много способов его применения рождается среди сообщества. В этой статье я покажу два самых главных способа использовать этот инструмент неправильно вместе со способами решения проблемы.


1. Вызов одного обработчика из другого обработчика

Рассмотрим следующую ситуацию. Допустим, вам надо обработать некоторое сообщение:

public class PingHandler : IRequestHandler<Ping, string>
{
    public Task<string> Handle(Ping request, CancellationToken token = default) =>
        Task.FromResult("Pong");
}

Затем вдруг пришли новые требования к логике (чтоб их...), и после обработки этого сообщения вам нужно запустить обработку других сообщений на основе результата работы исходного обработчика либо входящего сообщения. Как делает большинство в данной ситуации:

public class PingHandler : IRequestHandler<Ping, string>
{ 
    private readonly IMediator _mediator;
    
    public PingHandler(IMediator mediator) => _mediator = mediator;
    
    public async Task<string> Handle(Ping request, CancellationToken token = default)
    {
    	await _mediator.Send(new AfterPing(request), token);
    	return "Pong";
    }
}

Так делать плохо, забудьте такой bad practice.

В первую очередь это выглядит крайне запутанно. У MediatR итак проблемы с навигацией через IntelliSense, не надо их усугублять.

Также, стоит вспомнить, что by design этот инструмент служит внешним мостом к фактическому поведению вашего приложения, которое ещё и специфично для выбранной предметной области. Про это как раз обложка статьи.

Получается, что вызов обработки других сообщений изнутри обработчиков это антипаттерн применительно к самой библиотеке. Со слов разработчика библиотеки:

The indirection of a handler is good at the application level, but just got confusing once we got inside a handler (and it introduced coupling).

Ведь цель инструмента сделать точки входа на верхнем уровне приложения по модели "запрос-ответ", максимально минимизируя связанность. Ровно противоположно тому, что манифестирует код выше.

Есть много способов избежать этой проблемы и упаковать эту логику:

  • Возвращать данные для формирования сообщения и вызывать его "наверху", там где был вызов исходного обработчика (самый элементарный способ);

  • Отдельный класс;

  • Доменный сервис;

  • Метод расширения;

  • Специальный сервис;

  • Пайплайны MediatR;

  • и так далее.

2. Обработка множества сообщений в одном handler

Допустим у вас есть некоторый скоп сообщений для работы с одной и той же сущностью. Поскольку там плюс минус одни и те же зависимости вы решили не писать несколько обработчиков, а слить их в один, и получили что-то такое:

public class MyEntityRequestHandler : 
    IRequestHandler<CreateMyEntityRequest, MyEntity>, 
    IRequestHandler<GetMyEntityRequest, MyEntity>,
    IRequestHandler<GetAllMyEntitiesRequest, List<MyEntity>>
{
...
}

Опять же мы идём против идеи библиотеки: разделить приложение на набор неповторяющихся запросов для повышения его гибкости и лёгкости сопровождения. Такой подход это возврат к методике разработки с раздуванием интерфейсов, нарушающей ISP.

Ну и, со слов разработчика библиотеки:

Don't combine your handlers, keep them separate, reduce coupling across handlers.

Вывод

В рамках статьи были рассмотрены две главные ошибки с объяснением, почему это ошибки и как их избежать. Почему они главные? Дело в их распространённости и сути: они идут противоречат design principles, отражённым в документации.

Также об этой теме можно подробнее почитать в источниках:

  • раз;

  • два;

  • три;

  • четыре.


Ещё я веду telegram канал StepOne, где оставляю много интересных заметок про коммерческую разработку и мир IT глазами эксперта.

Источник: https://habr.com/ru/post/683804/


Интересные статьи

Интересные статьи

Работа в публичном облаке открывает широкие возможности гибкой разработки и доставки приложений. Так как компании и специалисты DevOps заинтересованы в развертывании кода и функций в кратчайшие сроки,...
Релиз .NET 6 вышел достаточно громким – если вы пишете под эту платформу, то навряд ли могли пропустить такое событие. Мы тоже не смогли пройти мимо и решили проверить, что интересного удастся найти...
По мере своего карьерного роста я все чаще и чаще испытываю чувство дежа вю. Во время личной или деловой встречи моему собеседнику достаточно упомянуть какой-то малозначительный факт — и я сразу же вс...
Начиная с 10 версии, перенести данные с одной базы PostgreSQL на другую несложно, с обновлением, без обновления — неважно. Об этом немало сказано и сказанное сводится к следующему: на м...
Всем привет! Не так давно на работе в рамках тестирования нового бизнес-процесса мне понадобилась возможность авторизации под разными пользователями. Переход в соответствующий р...