Предполагается, что доменные объекты в доменно-управляемом дизайне предназначены только для записи?

13

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

Один из выводов, к которому я начал приходить, особенно в ответ на прочтение более подробной информации об источнике событий и разделении ответственности по запросам команд (CQRS), заключается в том, что, возможно, доменные объекты предназначены для использования только в целях записи. Чтобы быть более ясным, кажется, что то, что люди тонко предлагают в большей части документации, которую я прочитал, что доменные объекты отвечают за выполнение ориентированных на домен операций / вычислений, проверки, а затем существуют в основном, чтобы обеспечить путь к постоянству через инфраструктура, предоставляемая в рамках реализации репозитория. Хотя мне действительно нравится тот факт, что это может значительно упростить модель предметной области, поскольку она снимает с себя ответственность разоблачения состояния.

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

  1. Как можно выполнить модульные тесты для объекта, который имеет сеттеры, или методы, которые изменяют состояние объекта, но не предоставляют внешне открытый интерфейс для чтения состояния, например, из методов получения свойств в C #? Можно ли выставлять состояние только для того, чтобы сделать этот объект тестируемым?
  2. Как показать пользователю результаты вычислений или операций, выполненных в домене, без необходимости их сохранения, а затем извлечь результаты из постоянного хранилища вне контекста домена? Можно ли выставлять состояние исключительно с целью показать результаты?

Является ли эмпирическое правило, что единственными получателями свойств (получателями доступа) должны быть те, которые также доступны для записи в домене? Или, иначе говоря, следует избегать только свойств, доступных только для чтения, поскольку они существуют только для целей чтения и, таким образом, не играют необходимой роли в реальной модели предметной области?

Материалы по теме:

  1. TDD, DDD и инкапсуляция
jpierson
источник

Ответы:

9

Не уверен, что есть «один верный путь» ответа для подхода к дизайну, который, честно говоря, все еще развивается. Во-первых, DDD и CQRS - это не одно и то же, хотя, похоже, люди CQRS произошли от начальной точки под влиянием DDD.

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

Возможно, вы наблюдаете некоторые споры о том, должны ли и как объекты домена быть изменяемыми, и какую функцию объект домена выполняет в системе в целом. CQRS разбивает систему на пути чтения и записи, поэтому разумно сделать вывод, что на самом деле вам не нужен доступ для чтения, когда вы находитесь на пути записи. Затем чтение становится чем-то, что вы делаете против событий, вызываемых одними объектами домена и потребляемых (обрабатываемых) другими. Если вы немного вернетесь в историю CQRS, вы найдете аргументы, что доменные объекты не должны иметь сеттеров, только геттеры и один метод «обработчик». Логика здесь заключается в том, что только потребление событий должно приводить к изменению состояния, и это изменение полностью обрабатывается внутренне объектом домена.

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

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

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

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

Опять же, вы запутались, потому что эти подходы сбивают с толку. Если вы хотите внедрить некоторые хорошие идеи в свое программирование, сначала попробуйте сочные кусочки. Границы DDD и явное определение ролей - это изменения в вашем мышлении, связанном с вашим кодом. CQRS как минимум предполагает, что чтение данных и запись данных являются разделяемыми операциями. Это понимание заставляет вас очень четко подумать о том, какова роль данных, которые вам нужно представить, сколько вам действительно нужно, кто их потребляет, насколько свежими они должны быть и т. Д. Вам не нужен полноценная реализация Event Sourcing для лучшей инкапсуляции в вашем коде. Вы можете начать, просто сосредоточившись на атомарных операциях внутри вашего объекта, подходы «говори, не спрашивай» к проектированию интерфейса объекта,

pfries
источник
1
+1 для начала с битами jucy. Кроме того: откусывание только CQS (пока пропуская часть 'событий') может быть хорошим началом.
Котцак
1

Предполагается, что доменные объекты в доменно-управляемом дизайне предназначены только для записи?

Нет. CQRS может использоваться вместе с DDD.

ним чимпский
источник
Но является ли часть Query в CQRS запросом данных, которые используются моделью предметной области для записи изменений в модель, или же она может использоваться для запроса данных для прикладного уровня, которые могут показывать значения для пользователя? Я слышал от некоторых, что DDD занимается координацией изменений и не должен использоваться для чтения с какой-либо другой целью, кроме как для координации изменений с другим объектом в модели предметной области. Решение, так или иначе, означало бы радикально отличающийся дизайн модели, видя, что открытые данные об объектах домена будут ограничены, если они будут использоваться только в домене.
jpierson
0

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

Вы также можете переопределить ToString()команды и события своего домена, чтобы обеспечить автоматическую, удобочитаемую отчетность о состоянии.

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

Эд Джеймс
источник
Не могли бы вы немного рассказать о том, применяется ли это по-прежнему, когда вы не используете источник событий?
jpierson
1
Без источника событий, возможно, мое предложение не будет работать; Я не знаю пути вашего кода. Для объекта домена, который в идеале не предоставляет никаких свойств, возможно, вы можете явно реализовать тестовый интерфейс, который предоставляет свойства, которые вы хотите протестировать. Только ваш тестовый код будет «знать», чтобы привести такие доменные объекты к тестовому интерфейсу.
Эд Джеймс
Спасибо за это предложение. Мне немного неловко от мысли изменить классы моего домена специально для тестируемости, но я полагаю, что это может быть одна из тех серых областей, которые все еще находятся в доменно-управляемом дизайне, если вы хотите, чтобы он был тестируемым. Другая мысль состоит в том, что если вы продемонстрируете стабилизируемость и тестируемость через один и тот же интерфейс, то, по крайней мере, вы вводите только одну зависимость инфраструктуры вместо двух. Что другие думают об этом?
jpierson