Вычисляемые значения и простые операции чтения - мучительная боль для моих проектов, управляемых доменом!

9

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

Пример:

Я возвращаю список Продуктов из своего хранилища через сервис. Этот список ограничен информацией о нумерации страниц из запроса DTO, отправленного клиентом. Кроме того, DTO указывает параметр сортировки (дружественное для клиента перечисление).

В простом сценарии все работает отлично: служба отправляет выражения подкачки и сортировки в репозиторий, а репо выдает эффективный запрос в БД.

Однако все это ломается, когда мне нужно отсортировать значения, сгенерированные в памяти из моей доменной модели. Например, класс Product имеет метод IsExpired (), который возвращает логическое значение на основе бизнес-логики. Теперь я не могу сортировать и пейджировать на уровне репо - все это было бы сделано в памяти (неэффективно), и мой сервис должен был бы знать тонкости, когда выдавать эти параметры в репо, а когда выполнять сортировку / разбиение по страницам. сам.

Единственный шаблон, который, по-видимому, имеет смысл для меня, - это сохранить состояние объекта в БД (сделать IsExpired () доступным только для чтения полем и обновить его с помощью логики домена перед сохранением). Если я разделю эту логику на отдельный репозиторий «read model / dto» и «report», я сделаю свою модель более анемичной, чем хотелось бы.

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

Я хотел бы услышать, как другие справились с этим, так как я уверен, что это обычное дело почти на проекте с участием DDD.

Дрогон
источник

Ответы:

3

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

Разделение моделей доменов команд и запросов часто поддерживается и имеет приятную аббревиатуру, которую вы можете найти в CQRS (разделение ответственности по запросам команд).

Использование модели доменной модели, Udi Dahan

Хотя в прошлом я был «успешным» в создании единой постоянной объектной модели, которая обрабатывала как команды, так и запросы, часто было очень трудно масштабировать ее, поскольку каждая часть системы изменяла модель в своем направлении.

Оказывается, разработчики часто предъявляют более жесткие требования, чем на самом деле нужно бизнесу. Решение использовать объекты модели домена для показа информации пользователю является именно таким примером.

[...]

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

CQRS с актерами Akka и функциональными моделями доменов, Дебашиш Гош

Грег Янг провел несколько отличных сессий по DDD и CQRS. В 2008 году он сказал: «Одна модель не может быть подходящей для отчетности, поиска и транзакционного поведения». У нас есть как минимум две модели - одна, которая обрабатывает команды и передает изменения в другую модель, которая обслуживает пользовательские запросы и отчеты. Транзакционное поведение приложения выполняется через модель агрегатов и репозиториев с расширенным доменом, а запросы обслуживаются непосредственно из ненормализованной модели данных.

CQRS, Мартин Фаулер

Изменение, которое вводит CQRS, состоит в том, чтобы разделить эту концептуальную модель на отдельные модели для обновления и отображения, которые, соответственно, называются Command и Query в соответствии со словарем CommandQuerySeparation. Обоснование состоит в том, что для многих проблем, особенно в более сложных областях, наличие одной и той же концептуальной модели для команд и запросов приводит к более сложной модели, которая ни к чему не приводит.

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

прецизионный самописец
источник
2

СПЕЦИФИКАЦИЯ

Я знаю, что вы уже приняли ответ, но вы спросили о DDD, и точное соответствие для этого - то, что Эванс называет «спецификацией»:
прямая ссылка на книги Google,
если эта ссылка не работает, проверьте книгу в этих результатах
Это страница 226, если у вас есть книга.

На стр. 227 приведены 3 варианта использования спецификаций: проверка, выделение, создание нового специального объекта. Ваш выбор - IsExpired.

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

В простом мире это означало бы размещение версии SQL в вашем хранилище и версии объектов в вашей модели, что, конечно, имеет недостатки. Логика в двух местах (плохо, кто-то забудет обновить их), и в вашем хранилище есть логика домена.

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

В противном случае вам придется создать специальный метод репозитория для этой спецификации, который будет использоваться из объекта спецификации. Вызовы для сбора объектов, соответствующих спецификации, будут идти через спецификацию, а не в хранилище. И, по крайней мере, код кричит «я в двух местах, не забывайте об этом» будущим сопровождающим. На странице 231-232 есть замечательный пример решения очень похожей проблемы.

Спецификация - это «разрешенная» утечка / проскальзывание «чистоты» DDD. Это все еще не может служить вашим потребностям для различных целей. Например, ORM может генерировать плохой SQL; там может быть слишком много дополнительного кодирования. Таким образом, вам, возможно, придется вызывать методы репозитория так, чтобы это почти было похоже на включение SQL в спецификацию. Плохая вещь, конечно. Но не забывайте, ваша программа должна работать с разумной скоростью. Это не должно выиграть приз чистоты DDD. Таким образом, реальность переключения хранилищ данных может означать старомодную хирургию во всей программе. Тоже плохо. Но не так плохо, как медленная (иначе сосущая) программа. Если запуск из разных БД является реальностью, очевидно, вы будете дублировать бизнес-правила для каждого хранилища данных для каждой спецификации. По крайней мере, вы держите руку на пульсе и можете использовать шаблон стратегии при обмене репозиториями. Но если вы используете конкретную БД уже помнитеYAGNI.

Относительно CQRS: цитата Фаулера от pdr выше все еще верна здесь: «наличие одной и той же концептуальной модели для команд и запросов приводит к более сложной модели, которая не очень хорошо работает» ... и вам, возможно, придется использовать CQRS или подобное. Но это намного дороже с точки зрения разработки и сопровождения. Если вы являетесь поставщиком пакетов, конкурирующим с другими, он может заплатить. Если вы пишете пользовательское LOB-приложение для одного покупателя, стремление к совершенству - плохой выбор. Вам нужно решить, стоит ли иметь дополнительные или полностью двойные модели, то стоит ли вам дополнительных усилий. СпецификацияЭто хороший компромисс, потому что он позволяет вам сделать это разделение только в одной маленькой части программы, которая нуждается в этом, с (скоростью) разработки и простотой одной модели. Удачи!

FastAl
источник
Это имеет смысл. Я думаю, что мне нужно прикусить пулю и прочитать книгу Эванса :-) Теперь я вижу, что поверхностное понимание этих концепций может действительно парализовать вас!
Дрогон
0

Я думаю, я бы задал вопрос, что такое бизнес-логика, которая определяет, является ли isExpired истинным или нет. Может ли эта логика быть выполнена запросом при наличии модели данных? Если да, можете ли вы сделать свой репозиторий достаточно интеллектуальным, чтобы использовать логику «isExpired», когда вы определенным образом запрашиваете его для Продуктов? Если нет, возможно, вам нужно пересмотреть свою модель данных.

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

Мэтью Флинн
источник