Дублирующая бизнес-логика во внешнем интерфейсе с внутренним компонентом микропроцессорной службы DDD
Вот абстрактный вопрос с последствиями для реального мира.
У меня есть два микросервиса; давайте назовем их CreditCardsService
и SubscriptionsService
,
У меня также есть SPA, который должен использовать SubscriptionsService
так что клиенты могут подписаться. Для этого SubscriptionsService
имеет конечную точку, где вы можете POST
модель подписки для создания подписки, и в этой модели creditCardId
это указывает на кредитную карту, которая должна заплатить за подписку. Существуют определенные бизнес-правила, которые указывают, можете ли вы использовать указанную кредитную карту для подписки (срок действия истек более 12 месяцев, это VISA и т. Д.). Эти конкретные бизнес-правила связаны с SubscriptionsService
Проблема в том, что команда, работающая в SPA, хочет /CreditCards
конечная точка в SubscriptonsService
возвращает все действительные кредитные карты пользователя, которые могут быть использованы в модели подписок. Они не хотят реализовывать те же правила проверки бизнеса в SPA, которые находятся в SubscriptionsService
сам.
Мне кажется, это идет вразрез с принципами SOLID, которые являются центральными для микросервисного проектирования; конкретно разделение интересов. Я также спрашиваю себя, какой прецедент это собирается создать? Мы собираемся добавить /CreditCards
конечная точка для службы OrdersService или любой другой службы, которая может использовать creditCardId
как свойство его модели?
Итак, главный вопрос заключается в следующем: как лучше всего это спроектировать? Следует ли дублировать логику проверки бизнеса между внешним и внутренним интерфейсом? Если эта новая конечная точка будет добавлена к SubscriptionsService
? Должны ли мы попытаться упростить бизнес-логику?
4 ответа
В микросервисах и DDD служба подписок должна иметь конечную точку кредитных карт, если это данные, которые имеют отношение к ограниченному контексту подписок.
Конечная точка кредитных карт может служить немного иной модели данных, чем вы могли бы найти в самой службе кредитных карт, потому что в контексте подписок кредитная карта может выглядеть или вести себя по-другому. Служба подписок будет иметь таблицу кредитных карт или хранилище резервных копий, вероятно, для поддержки хранения собственной схемы кредитных карт и ссылки на некоторый источник правды, чтобы поддерживать эти данные в хорошем состоянии (например, сообщения о событиях карты в шине или другие механизм).
Это позволяет 3 вещи, во-первых, служба подписки не будет полностью отключена, если карты некоторое время не работают, она может ссылаться на свою собственную таблицу и работать в любом случае. Во-вторых, код вашего домена будет более целенаправленным, поскольку он будет иметь дело только со свойствами кредитных карт, которые действительно имеют значение для решения текущей проблемы. Наконец, если ваш магазин карт может даже иметь дополнительные специфичные для домена свойства, которые вычисляются и материализуются в магазине.
Обязательная ссылка Фаулера: шаблон ограниченного контекста
Это совершенно справедливый запрос, и вы должны предложить эту конечную точку. Если вы определяете правила того, какой CC действителен для вашей услуги, вам следует также предложить любую помощь в этом вопросе.
Логика не должна повторяться. Это делает системы неприемлемыми.
Это имеет меньшее отношение к SOLID, хотя SRP также сказал бы, что если вы несете ответственность за что-либо, то любая связанная логика также принадлежит вам. Эта проблема не может быть отделена от вашего сервиса, так как он определен там.
Как вариант решения, я бы, возможно, посмотрел, смогу ли я избежать связывания со службой CC, поскольку она у вас уже есть. Могу ли я перенаправить клиента с созданным запросом, возможно, в службу CC, чтобы получить все соответствующие CC, фактически не зная их в службе подписки.
Каков наилучший способ разработать это? Следует ли дублировать логику проверки бизнеса между внешним и внутренним интерфейсом? Нужно ли добавить эту новую конечную точку в сервис SubscriptionService? Должны ли мы попытаться упростить бизнес-логику?
С моей точки зрения, я бы интегрировал "Subscription BC" (S-BC) с "CreditCards BC" (CC-BC). CC-BC находится в восходящем направлении, а S-BC - в нисходящем. Вы можете сделать это с помощью REST API в CC-BC или с помощью очереди сообщений.
Но то, что я проверяю, является операцией, выполненной с CC, а не самой CC, то есть проверяется, "является ли эта CC действительной для подписки". И эта проверка в S-BC.
Если SPA хочет получить "CC пользователя, которые он / она может использовать для подписки", это функциональность S-BC.
Клиент (SPA) должен вызвать API S-BC, чтобы использовать эту функциональность, а S-BC выполняет функциональность, получая CC от CC-BC и выполняя проверку.
Даже когда источником правды является модель предметной области, и в конечном итоге вы должны пройти валидацию на уровне модели предметной области, валидация все равно может выполняться как на уровне модели предметной области (на стороне сервера), так и в пользовательском интерфейсе (на стороне клиента). Проверка на стороне клиента - большое удобство для пользователей. Это экономит время, которое они могли бы потратить на ожидание возврата к серверу, который может вернуть ошибки проверки. С точки зрения бизнеса, даже несколько долей секунд, умноженных сотни раз в день, тратят много времени, денег и разочарований. Простая и немедленная проверка позволяет пользователям работать более эффективно и производить более качественный ввод и вывод. Так же, как модель представления и модель предметной области различны, проверка модели представления и проверка модели предметной области могут быть схожими, но служат другой цели. Если вас беспокоит DRY (принцип "Не повторяйте себя"), учтите, что в этом случае повторное использование кода также может означать связывание, и в корпоративных приложениях более важно не связывать серверную сторону с клиентской, чем следовать СУХОЙ принцип. (Книга NET-Microservices-Architecture-for-Containerized-NET-Applications)