Слои DDD и внешний API - PullRequest
0 голосов
/ 06 июля 2019

Недавно я пытался заставить мое веб-приложение использовать отдельные слои.

Если я правильно понимаю концепцию, мне удалось извлечь:

  1. ДоменСлой Здесь находятся мои основные доменные сущности, совокупные корни, объекты-значения. Я вынужден иметь чистую модель предметной области, то есть здесь у меня нет определений сервисов.Единственное, что я здесь определяю, это репозитории, которые на самом деле скрыты, потому что инфраструктура Axon реализует это для меня автоматически.

  2. Уровень инфраструктуры Здесь Axon реализует определения репозитория для моих агрегатов вслой домена

  3. Уровень проецирования Здесь реализованы обработчики событий для проецирования данных для модели чтения с использованием MongoDB для ее сохранения.Он не знает ничего, кроме модели событий (простые классы данных в kotlin)

  4. Прикладной уровень Здесь начинается путаница.

  5. Контроллерслой Здесь я реализую контроллеры GraphQL / REST, этот уровень контроллера использует модель команд и запросов, что означает, что он обладает знаниями о командах уровня домена, а также о модели запросов уровня проекции.

Как я уже упоминал, путаница начинается с прикладного уровня, позвольте мне немного объяснить это на упрощенном примере.

Учитывая, что я хочу, чтобы модель предметной области реализовала логику борьбы с покемонами.Мне нужно использовать PokemonAPI, который предоставил бы мне данные статистики имен Pokemon и т. Д., Это был бы внешний API, который я использовал бы, чтобы получить некоторые данные.

Допустим, я бы реализовал домен следующим образом: (Имейте в виду, что я расширил эту реализацию, поэтому она вызывает некоторые проблемы, которые у меня возникают в моем собственном домене)

Pokemon {
  id: ID 
}
PokemonFight {
  id: ID
  pokemon_1: ID
  pokemon_2: ID

  handle(cmd: Create) {
    publish(PokemonFightCreated)
  }

  handle(cmd: ProvidePokemonStats) {
    //providing the stats for the pokemons
    publish(PokemonStatsProvided)
  }

  handle(cmd: Start) {
    //fights only when the both pokemon stats were provided
    publish(PokemonsFought)
  }

Поток данных между слоями будет выглядеть следующим образом.

Пользователь -> [HTTP] -> Контроллер -> [CommandGateway] -> (Приложение | Домен) -> [EventGateway] -> (Приложение| Domain)

Предположим, что два покемона созданы, и сценарий использования боя покемонов в основном состоит в том, что при его создании предоставляется статистика, а затем, когда предоставляется статистика, бой автоматически начинается.

Эта логика варианта использования может быть решена с помощью обработчика событий или даже саги.

Однако, как вы видите в агрегате PokemonFight, есть команда [ProvidePokemonStats], которая в основном предоставляет их статистику, как и мой домен.не знаю, как получить такие данные, эти данные предоставляются с PokemonAPI.

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

Если я подумаю об этом, единственная цель, с помощью которой обработчик саг / событий, который живет на прикладном уровне, состоит в том, чтобы предоставлять надлежащие данные моему домену, чтобы он могпродолжить с его вариантами использования.Поэтому, когда происходит сбой внешнего API, я отправляю команду в домен, и затем он может решить, что делать.

Например, я мог бы просто поместить каждый обработчик саг / событий в приложение, поэтому, когда я решу изменить некоторую автоматизациюЯ точно знаю, какой модуль мне нужно отредактировать и где его найти.

Другая путаница - это когда у меня несколько доменов, и я хочу создать сценарий использования, который использует многие из них и соединяет данные между нимив моем мозгу сразу же прозвучало, что это должен быть прикладной уровень, который будет использовать доменные API для управления прецедентом, потому что я не думаю, что мне следует добавлять зависимость другого домена в основной.

TL; DR

  1. Какой уровень должен отвечать за реализацию автоматизированного процесса между агрегатами (может быть единичным, но вы понимаете, что я имею в виду), если процессу требуются некоторые внешние данные API.

  2. Какой уровень должен отвечать за внедрение автоматизированного процесса между агрегатами, которые живут в разных доменах / микросервисах.

Заранее спасибо, и мне также жаль, если то, что я написал, звучит сбивающим с толку или слишком много текста, однако любые ответы о размещении приложений DDD и правильном расположении компонентов я был бы очень признателен.

Ответы [ 2 ]

1 голос
/ 08 июля 2019

Я согласен, что ваша формулировка может быть немного расплывчатой, но у меня всплывают некоторые вещи, которые могут направить вас в правильном направлении.Имейте в виду, формулировка делает это так, что я не на 100% уверен, что это то, что вы ищете.Если это не так, пожалуйста, прокомментируйте и исправьте мой ответ, который я предоставлю, чтобы я мог соответствующим образом обновить его.

Теперь, перед вашим актуальным вопросом, я сначала хотел бы указать на следующее.Я предполагаю, что вы смешиваете понятие сообщений и вашей доменной модели, принадлежащих одному слою.Лично для меня сообщения (или ваши команды, события и запросы) являются вашим открытым API .Это язык, на котором говорит ваше приложение, поэтому он должен быть свободно доступен для любого компонента и / или службы в пределах вашего ограниченного контекста.

Таким образом, любой компонент в вашем«прикладной уровень», содержащийся в том же ограниченном контексте, должен иметь возможность знать об этом общедоступном API.Ответственным за API будет ваша модель предметной области, это правда, но эти понятия должны быть общими, чтобы иметь возможность общаться друг с другом.

Тем не менее, компонент, который предоставит состояния вашимЯ думаю, что агрегат можно рассматривать в двух направлениях.

  1. Это компонент, который обрабатывает определенную команду «Начать совпадение покемонов».Этот компонент обладает умом знать, чтобы сначала получить состояния, прежде чем он сможет отправлять команды Create и ProvidePokemonStats, таким образом гарантируя, что он будет последовательно создавать рабочее совпадение со статистикой в ​​нем , а не * 1021.* отправка любого из обоих внешних API извлечения статистики не удалась.
  2. Ваш вопрос в этом вопросе - иметь компонент обработки событий, который реагирует на создание соответствия.Отсюда я бы сказал, что будет создана недолгая сага, так как вам придется иметь дело со сценарием ошибки, когда вы не сможете получить статистику.Обычный обработчик событий, скорее всего, склонен решать эту проблему правильно.

Независимо от двух выбранных вами опций, этот сервис будет обрабатывать сообщений, или ваш публичный API.Как таковой, он находится внутри вашего приложения, а не как компонент, с которым другие будут иметь дело напрямую.Два разных приложения / микросервиса только в большей степени предполагают, что вы говорите о двух разных ограниченных контекстах.Конечно, тогда будет сага для координации операций между обоими контекстами.Обратите внимание, что между ограниченным контекстом вы хотите делиться сознательно, когда речь заходит об общедоступном API, так как в идеале вы не хотели бы открывать все для внешнего мира.

Надеюсь, это поможет вам, а если нет, как я сказалПожалуйста, прокомментируйте и дайте мне указания, как правильно ответить на ваш вопрос.

1 голос
/ 06 июля 2019

Я постараюсь объяснить это.Если вы используете CQRS:

  • На стороне записи (команды): Службы приложений являются обработчиками команд.Обработчик cmd обращается к домену (репозитории, агрегаты и т. Д.) Для реализации варианта использования.

    Если для варианта использования требуется доступ к данным из другого ограниченного контекста (микросервис), он использует сервис инфраструктуры (через внедрение зависимостей).Вы определяете интерфейс службы инфраструктуры на уровне службы приложений, а реализацию на уровне инфраструктуры.Затем инфраструктура получает доступ к удаленному микросервису через http rest, например.Или интеграция с помощью событий.

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

Надеюсь, это поможет.

...