Параметры сервисной службы/возвращаемые типы

Я работаю над стандартным веб-приложением с доменом, организованным по концепциям DDD. Мне интересно, какие объекты мои приложения должны принимать и возвращать. Скажем, у меня есть служба приложений для User aggregate.

1) DTOs/простые типы (строка, int и т.д.)

public interface UserApplicationService {
  void registerUser(UserDTO userDTO);
  List<UserDTO> getUsersForOrganization(String organizationId);
}

В этом случае служба приложения отвечает за вызов ассемблера, который преобразует DTO в объекты домена и наоборот.

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

Недостатком является то, что в случае форм проверка должна основываться на DTO. Поэтому мои правила проверки дублируются между доменом (объект отвечает за его состояние) и правилами проверки DTO. (Как в случае Spring пример приложения MVC). Кроме того, если для некоторых частей зрения требуется другая форма модели (скажем, у UserDTO недостаточно информации для визуализации), мне нужно будет создать еще один DTO и базу на пару DTO, возвращенных из службы приложений, составить еще одну, используемый для просмотра.

2) Типы доменов

public interface UserApplicationService {
  void registerUser(User user);
  List<User> getUsersForOrganization(OrganizationId organizationId);
}

В этом случае контроллер/ведущий отвечает за преобразование.

Большой недостаток заключается в том, что объекты моего домена протекают из службы приложений - нет четкого разделения. Кроме того, где наши границы транзакций? Доменные объекты, к которым может быть присоединен, например, сеанс Hibernate, утечки вне уровня служб приложений. (Тем не менее, я заметил, что это то, как написано множество примеров приложений.)

Преимущество может заключаться в том, что диспетчер/ведущий отвечает за подготовку модели для просмотра, поэтому он может составлять DTO на основе требований к представлению. Например, для просмотра может потребоваться дополнительная информация, которая не возвращается в DTO из #getUsersForOrganizationMethod. Кроме того, проверка может основываться на объектах домена, поэтому она не дублируется между DTO и объектами домена.

3) Объекты домена + фасад

Это третий вариант, используемый в приложении DDDsample. Службы приложений возвращают типы доменов, но есть некоторый фасад, который отвечает за преобразование. Таким образом, в моем случае, диспетчер/ведущий разговаривает с фасадом с DTO, фасад делает трансформацию и разговаривает с приложениями, использующими объекты домена. Однако, по моему скромному мнению, это кажется немного подавляющим - слишком много слоев, слишком много шаблонов. Для одного приложения это может показаться здоровым, но если у нас их было десятки, нам нужно иметь одинаковое количество фасадных методов - чистое дублирование. Кроме того, где границы транзакций?

Ответ 1

+1

Я предпочитаю смешанное решение.

1) Я использую объекты домена в качестве аргументов, но ограничены ValudObject. Я считаю, что жизненный цикл Entity должен быть тщательно обработан, и большую часть времени представление не имеет достаточного значения для заполнения всего Entity, за исключением очень простых приложений CRUD. Я видел много раз, что некоторые разработчики инициализируют Entity конструктором небрежно и заполняют их только частью полей, которые необходимы для конкретной функции, поэтому очень легко ввести ошибки для NullPointerException и бедного парня, который назначается для исправления проблемы, необходимо найти несколько десятков методов, чтобы найти, где было создано Entity. Объект s извлекается из Репозитория или создается Factory в моих проектах.

Иногда я использую некоторые объекты формы в качестве параметра для простоты.

2) Я использую контроллер mvc для преобразования объектов домена, возвращаемых службой приложения, в ViewAdapter (компонентный компонент отделяет модели домена от ui), иногда отлаживать работу нужно здесь.

3) Я использую Фасад только тогда, когда служба приложения должна быть открыта посредством удаленного вызова процедур, такого как веб-сервис. Dto s используются в качестве аргументов и типов возврата в этом случае, а Фасад отвечает за преобразование DTO и Модель домена. > .

4) Валидация неудобна, если служба приложения должна быть подвержена как веб-просмотру, так и удаленным вызовам процедуры. Это приводит к дублированию проверки на обоих объектах формы и Dtos. Я проверяю только простые ограничения (не null, длина, чтобы назвать несколько, бизнес-правила проверяются программными объектами домена), потому что я еще не нашел идеального решения.

Надеюсь, что это поможет, и если есть лучшие решения, сообщите мне, пожалуйста.

Update1:

1) Я должен признаться, что я не гуру в этой области, и я тоже пытаюсь найти хорошее решение. Поэтому иногда в моем текущем решении есть некоторая несогласованность, например, форма bean, которую вы упомянули в комментарии. Иногда мы принимаем какую-то форму bean как Command и помещаем в нее некоторую логику домена, поэтому в этом случае они принадлежат слою Domain.

2) Граница транзакции находится в сервисе приложения. Технически, объект домена может быть изменен вне границы непреднамеренно. Мы рассматриваем это по командной дисциплине и обзору кода.

Ответ 2

Обычно я склоняюсь к приближению к одному, используя команды и запросы.

Вот фрагмент сообщения в блоге, которое я публикую в эти выходные.

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

Вы можете найти пример этого в моем блоге; http://www.jefclaes.be/2013/01/separating-command-data-from-logic-and.html.

Что касается ваших опасений относительно валидации, имейте в виду, что повторение дублирования не является ужасным; http://gorodinski.com/blog/2012/05/19/validation-in-domain-driven-design-ddd/.

Ответ 3

Я использую несколько из трех ваших подходов, но очень последовательно, придерживаясь принципов иерархии, которые описывает Эрик Эванс в DDD синяя книга.

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

Я объясняю свои причины для использования этого подхода в моем ответе на очень похожий вопрос: fooobar.com/info/77571/...