как другие делают: изучил примеры проектов на github, выступления на тему архитектуры web api и тд. Понравилась идея из vertical slice architecture, где на каждый запрос есть обработчик, который обрабатывает только этот запрос, вместо сервисов, которые делают все и сразу. Однако категорически не понравился MediatR, но примеров с таким подходом без MediatR не нашел. Хотел узнать ваше мнение, почему MediatR так часто используется в примерах по архитектуре?
Чем не понравился? А используют потому, что можно ему делегировать передачу управления, при этом запрос и хендлер могут находиться в разных сборках и не знать ничего о друг друге. Ну и в последних версиях сделали пайплан для пре/пост операций
Pipeline штука крутая, но в asp.net core он теряет актуальность, так как уже имеется более мощный механизм middleware. А это вроде как основная фишка MediatR. Аргумент, что команды и обработчики могут в разных сборках находится не понял. Им же все равно нужно знать о друг друге, так как handler наследуется от базового класса с обобщенным параметром request'а. Там вроде можно все через object передавить, но это как-то не очень. Это техническая часть, у меня еще претензия к логической. Зачем так сильно отделять место отправки request'а от handler'а? То есть чтобы искать Handler определенного request'а нужно искать непонятно где. Handler - это просто класс с одинм методом execute, почему бы просто не вызвать напрямую.
Давай ты почитаешь про middleware https://docs.microsoft.com/ru-ru/aspnet/core/fundamentals/middleware/?view=aspnetcore-5.0 Аргумент по разделению на сборки: выделяешь общий проект и хранишь там реквесты и/или ивенты для медиатора. Подключаешь его к 2 не связанным проектам(например, 2 домена) и через медиатр отправляешь реквест куда-то, а хендлер этого реквеста его обрабатывает, но уже у себя, и он не знает кто ему прислал запрос. Это лишь один из кейсов Отделять бизнес логику от внешнего запроса стоит в больших проектах, т.к. закладывается масштабируемость
Не знаешь, что конкретно про pipeline asp.net core и middleware я должен вычитать. Я правильно понимаю, что разделение на сборки это чисто логическое действие, то есть с технической стороны одна сборка все равно будет иметь ссылку на другую?
Все что в ссылке Да, это логическое разделение Можно и в одной сборке все сделать, но поддерживать будет не удобно, но никто не запрещает так делать Сборка не будет ссылаться на другую до тех пор, пока ты не скажешь это делать
Все равно, как-то не убедительно. Смотрится так, как будто добавляется сложность в проект просто потому что можно. Возможно, это какая-то узко специализированная штука, нужная в случаях, которых я не встречал. Однако я пока не вижу причин, почему это используется почти в каждом примере/template'е. Спасибо за потраченное время на объяснение.
Сложность возрастёт от постоянных требований, и когда настанет момент что ради добавления доп проверки тебе потребуется пробросить во множество мест ссылку, вот тогда и придет понимание что делаешь что-то не то Если пет проект не большой, то и забей на медиатр. Мб потом прикрутишь, если потребуется. Главное выноси бизнес логику из контроллера
А есть похожие подходы к организации проекта? Мне-то понравилась идея: один класс - один метод - один use case. Однако изучение github'а показало, что большинство примеров попадают в три категории: что-то много всего делают контроллер; контроллер вызывает один метод сервиса и что-то помелочи; MediatR. Пока собираюсь делать свой mini mediatr, как я его вижу, но может уже есть что-то более обкатанное.
Ну сейчас популярен cqrs, но там mediatR используется, в основном. Можно свой написать, но зачем ещё один велосипед?
а что именно не понравилось
Если ещё интересно. 1) Pipeline behaviors повторяют логику middlewares из asp.net core. Вроде бы ключевая фишка библиотеки, но оказывается не нужна в силу встроенного механизма. 2) mediatr.send(req) - фиг пойму что и куда отправилось. Мне пытались объяснить, что это плюс, мол, application layer сам решит что и куда, но я так не считаю. Указать явно, какой handler будет вызван, разве так сложно? Сразу понятно что и куда отправляется. Как итог, дебажить проще и F12 просто работает. 3) Конкретно я пока не вижу хорошего случая для использования notification. В самом простом случае, остаётся только два полезных интерфейса: IRequest и IRequstHandler. Пишу не на js, так что для двух интерфейсов (один из которых marker) отдельная библиотека не нужна. Пока планирую IRequestHandler получать в action методах через [FromServices] и напрямую вызывать Handle(). Однако проект ещё не начат, может что получше придумаю.
1. Это похожие механизмы, но работают они на совершенно разных уровнях абстракции. Пайплайн аспнета описывает как обрабатывать запрос. Он оперирует терминами веб сервера, и ответственность у него заканчивается на этом уровне. Простейший пример, ты не можешь из этого пайплайна создать транзакцию, опубликовать событие произошедшее в системе — это безумие. Медиатор работает на уровне слоя приложения и пайплайн там позволяет решать именно задачи приложения. Ему без разницы каким образом иницирована операция: вызвана по таймеру, прилетело сообщение в шину, пришел запрос с HTTP. 2. Наиболее просто дебажить программы которые написаны в одном файле, чуть менее глупый пример: вся логика в контроллере. Никуда не нужно прыгать, все видно сразу. Но разработчики почему то все продолжают распихивать по классам и придумывать SOLID. И делают это не просто так. В случае медиатора квери и команды и публичный интерфейс твоего приложения. Ты экспозишь только их и говоришь, что вот с моим приложением можно взаимодействовать только так. Хендлеры это детали реализации. Зачем какому-то контроллеру веб апихи про них знать? Он работает только с интерфейсом. 3. Не обязательно использовать все фичи, но notification удобны, например, в DDD для публикации доменных событий. Ты действительно можешь написать свою реализацию, но люди предпочитают использовать библиотеки, потому что эти библиотеки поддерживают и развивают независимо от тебя. В них чинят баги, про них знает больше разработчиков. И если на твой проект придет новый человек, ему не придется разбираться в велосипеде (собственной реализации), а про медиатор многие знают и имеют опыт использования.
1) Частично соглашусь. Изначально я приписывал напрямую работу с примитивами веб запросов, как плюс, так как не имеет смысла использовать что-то другое, если встроенный механизм справляется не плохо с поставленной задачей. Для полноты картины хотелось бы уточнить, что тут имеется в виду под транзакцией. И как в таком случае определяется, какой конкретно pipeline ответственен за определенный функционал? К примеру, мне нужно реализовать audit логи. Оба pipeline способны реализовать данную функциональность, но чья это обязанность в таком подходе? 2) Спорный вопрос. Я теперь понимаю, что IRequest воспринимается больше как публичный контракт для потребителя, но все равно считаю, что эта не та абстракция, которая должна быть спрятана за каким-то интерфейсом. В этом вопрос явный вызовом мне кажется более простым и элегантным решением. Думаю, этот пункт можно вычеркнуть, он более субъективный. 3) Возможно. Еще не работал с настолько сложными приложениями, чтобы DDD был необходим. 4) Возвращаясь к изначальному вопросу. MediatR все-таки та самая библиотека, которую можно использовать в 90% случаев, как это может показаться из примеров в интернете? 5) И в каких случаях стоит рассматривать что-то другое?
Медиатр стоит использовать в том случае, если ты решил строить интерфейс приложения через команды и квери. Это далеко не 90% случаев.
А если я решил использовать организацию "folder-by-feature", но не вижу плюсов в разделении запросов записи и запросов чтения, что использовать?
Ну заведи на каждую фичу по сервису и дергай сервис
Ок, спасибо за развернутые ответы.
Обсуждают сегодня