Отображение данных об интерфейсе пользователя в гексагональной архитектуре
Я изучаю DDD и шестиугольную архитектуру, я думаю, что получил основы. Однако есть одна вещь, которую я не знаю, как решить: как я показываю данные пользователю?
Так, например, я получил простой домен с сущностью Worker с некоторыми функциональными возможностями (некоторые методы вызывают изменение сущности) и WorkerRepository, чтобы я мог сохранять Workers. Я получил прикладной уровень с некоторыми командами и командную шину для управления доменом (например, создание рабочих и обновление их рабочего времени, сохранение изменений), а также уровень инфраструктуры, на котором есть реализация WorkerRepository и приложение с графическим интерфейсом.
В этом приложении я хочу показать всем работникам некоторые из их данных и быть в состоянии изменить их. Как мне показать данные?
- Я мог бы дать ему ссылку на реализацию WorkerRepository. Я думаю, что это не очень хорошее решение, потому что таким образом я мог бы добавить новых рабочих в хранилище, пропуская командную шину. Я хочу, чтобы все изменения проходили через командную шину.
- Хорошо, тогда я бы разделил WorkerRepository на WorkerQueryRepository и WorkerCommandRepository (согласно CQRS) и дал ссылку только на WorkerQueryRepository. Это все еще не хорошее решение, потому что репозиторий возвращает рабочие объекты, у которых есть методы, которые их изменяют, и как эти изменения будут сохраняться?
- Должен ли я создать два типа репозиториев? Один будет использоваться на уровне домена и приложений, а другой будет использоваться только для предоставления данных внешнему миру. Второй не будет возвращать полноценные сущности Worker, только WorkerDTO, содержащие только те данные, которые нужны GUI. Таким образом, GUI не имеет другого способа изменить Workers, только через командную шину.
Третий подход - правильный путь? Или я неправильно заставляю что изменения должны проходить через командную шину?
1 ответ
Должен ли я создать два типа репозиториев? Один будет использоваться на уровне домена и приложений, а другой будет использоваться только для предоставления данных внешнему миру. Второй не будет возвращать полноценные сущности Worker, только WorkerDTO, содержащие только те данные, которые нужны GUI.
Это подход CQRS; это работает довольно хорошо.
CQRS - это просто создание двух объектов, где ранее был только один объект. Разделение происходит в зависимости от того, являются ли методы командой или запросом (то же определение, которое используется Мейером в разделении команд и запросов, команда - это любой метод, который изменяет состояние, а запрос - это любой метод, который возвращает значение).
Текущий термин для WorkerDTO, который вы предлагаете, - "Проекция". У вас часто будет больше одного; то есть вы можете иметь отдельную проекцию для каждого представления работника в графическом интерфейсе. (Это имеет изящный побочный эффект, облегчающий представление - ему не нужно думать о данных, которые ему предоставляются, потому что данные уже отформатированы с пользой).
Другой способ думать об этом, это то, что у вас есть представления "только для записи" (совокупность) и "только для чтения" представления (проекции). В обоих случаях вы читаете текущее состояние из книги записей (через репозиторий), а затем используете это состояние для создания необходимого представления.
Поскольку модели для чтения не нужно сохранять, вам, вероятно, лучше думать на фабрике, а не в репозитории на стороне чтения. (В 2009 году Грег Янг использовал "провайдера" по той же причине.)
После того, как вы сделали первый шаг разделения двух объектов, вы можете начать рассматривать их разные варианты использования независимо.
Например, если вам нужно уменьшить производительность чтения, у вас есть возможность реплицировать книгу записей на кучу подчиненных копий, и ваша фабрика проекций загружается из подчиненных, а не из основных. Или начать исследовать, является ли более подходящим другое хранилище постоянства (хранилище значений ключей, база данных графа, полнотекстовый индексатор). Уди Дахан рассматривает ряд этих идей в CQRS - но разные (2015).
"читать модели не нужно сохранять" не правильно.
Это правильно; но, возможно, это не так ясно и конкретно, как могло бы быть.
Нам не нужно создавать долговременное представление модели чтения, потому что вся информация, которая описывает дисперсию между экземплярами модели чтения, уже была захвачена нашими записями.
Мы часто хотим кэшировать модель чтения (или ее представление), чтобы мы могли амортизировать работу по созданию модели чтения по многим запросам. И различные компромиссы могут указывать на то, что кэшированные представления должны храниться длительно.
Но если метеор появляется и разрушает наш кэш моделей чтения, мы теряем трудозатраты, но не теряем информацию.