В последнее время я много читаю о DDD. У меня были некоторые базовые знания (и я использовал их на практике), но теперь я решил перейти (почти) на 100% DDD. Конечно, сразу возникли проблемы.
У меня есть 3 слоя для каждого модуля (функции): приложение, домен и инфраструктура. Я использую шаблон гексагональной архитектуры, что в основном означает, что я получил свою базовую логику в доменных классах, уровень приложения использует ее (но уровень домена вообще не знает о уровне приложения), инфраструктура реализует мои порты из домена (репозитории db) и некоторые интерфейсы из уровня приложения и т. д.
Теперь, когда я обрабатываю какой-то вариант использования в службах приложений, мне приходится работать с моим корневым агрегатом, выполнять некоторую логику и, наконец, сопоставлять его с некоторым DTO для пользовательского интерфейса. Проблема в том, что для выполнения такого сопоставления я должен предоставить геттеры / сеттеры для большинства моих атрибутов, что меня убивает. Я хочу избежать анемичной модели, предоставляя множество бизнес-методов и очень мало геттеров / сеттеров.
Я вижу 2 решения:
Какие-нибудь другие решения, как вы, люди, справляетесь с такой распространенной проблемой в своих приложениях? Я знаю, что на самом деле действительно сложно использовать чистый DDD, и нормально не следовать всем правилам, но я заметил, что наличие как можно меньшего количества геттеров / сеттеров очень помогает мне в моем дизайне, но в то же время ясно, что DTO не принадлежит домену вообще.
Да точно, но какая альтернатива?
Я могу сказать вам, что делаю, но не уверен, что это именно тот ответ, который вы ищете. Я помещаю DTO на уровень представления. На уровне обслуживания работайте с моделью и во время возврата результата создайте и заполните DTO. Для получения данных из модели я использую геттеры. И у DTO есть все общедоступные атрибуты без методов.
Вам не нужны сеттеры, просто геттеры, чтобы прочитать это




The problem is that to perform such mapping I have to provide getters/setter for most of my attributes which kills me.
Ага - я долго боролся с этим. Настоящий ответ заключается в том, что магии нет.
Если вы хотите, чтобы агрегат был полезен, вам нужно иметь возможность получать из него информацию как-то. Базы данных только для записи не очень интересны; если в интерфейсе нет некоторого запрос, тогда нет особого смысла помещать информацию в вещь в первую очередь.
Допускается специфичный для домена запрос на получение ценить из агрегата. Ключевые ограничения
Не должно быть возможности изменить состояние агрегата, манипулируя возвращаемым значением. Поэтому мы стремимся возвращать либо объекты, у которых нет мутаторов, либо копии этих объектов.
Мы не должны поощрять идиомы, в которых потребитель запрашивает нас, выполняет некоторые операции с результатом запроса, а затем выбирает команду на основе результата этих операций.
// Не делайте этого: int x = o.X (); х = х + 1; o.Y (x)
Есть несколько вещей, которые вы можете сделать, чтобы код в целом выглядел «чище».
1) Пусть агрегат отвечает на запросы с объектами значений, а затем запрашивает у этих объектов значений информацию, необходимую для создания вашего DTO.
2) Передайте фабричный метод в агрегат, чтобы получить нужные вам данные.
<T> T query(API<T> api)
Где API<T> - это конструктор / фабрика, с которой агрегат может взаимодействовать.
3) Иметь два отдельных интерфейса, реализованных агрегатом (один для запросов, другой для команд), и предоставлять вызывающей стороне доступ только к тому интерфейсу, который им нужен.
4) Имейте единую точку запроса, чтобы получить «текущее состояние» из агрегата, а затем построить все остальное из этого.
Вон Вернон рассматривает это в главе 14 (применение) красной книги, в разделе «Пользовательский интерфейс» (стр. 512) он предлагает некоторые альтернативы:
Надеюсь, это поможет.
Применяя CQRS, вы можете избежать геттеров в вашем AR. Кроме того, вы даже можете уменьшить количество атрибутов, которые есть в вашем AR, до тех, которые необходимы для удовлетворения некоторого инварианта.
Позвольте мне лучше объяснить.
CQRS (разделение ответственности за запросы команд) полностью различает операции команд и запросов, которые предоставляет ваше приложение.
Командные операции изменяют состояние вашего приложения, выполняя некоторый вариант использования. Результатом выполнения Команды является Доменное событие, которое содержит информацию об изменении.
Затем событие домена (которое моделируется как DTO) используется для построения вашей модели представления, которая содержит все данные, которые вам нужно вернуть из ваших запросов. Модель представления - это DTO, который легко сериализовать и отправить по сети.
Следовательно, ваши операции Query (те, которые используются для заполнения вашего пользовательского интерфейса информацией) даже не увлажняют ваши AR. Они работают с вашей моделью представления, поэтому любые изменения в ней не влияют на вашу AR.
Наконец, как я упоминал в начале, большую часть времени у нас есть множество атрибутов в наших AR только для того, чтобы сохранить эту информацию в нашей базе данных. Однако эта информация никогда не используется для удовлетворения какого-либо инварианта. Например, когда мы сохраняем имя пользователя, но никогда больше не используем эти данные ни в каком другом варианте использования. Если информация уже сохраняется в событии домена и проецируется в вашей модели представления, вы можете запросить ее, чтобы избежать наличия этого поля в своем AR, сохраняя их как можно более чистыми.
Я бы избегал обоих, потому что у вас может быть много DTO, пересекающих разные модели. DTO может нести имя пользователя от Пользователя, а также набор улиц и номеров из Адреса. В то время как другой DTO может иметь имя и фамилию пользователя, а также количество посещений веб-сайта.