DDD подход к базовым операциям CRUD в сложном доменно-ориентированном приложении

9

Моя компания переписывает наше веб-приложение с нуля. Это крупное приложение уровня предприятия со сложной областью в финансовой индустрии.

Мы используем ORM (Entity Framework) для сохранения.

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

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

Когда пользователь переходит к экрану редактирования, например, для изменения инвестиционного счета, поля на экране - это точные поля, хранящиеся в базе данных, а не представление домена, используемое позже для расчетов. Так зачем мне загружать представление домена инвестиционного счета, когда экрану редактирования нужно представление базы данных (необработанные данные)?

После того, как пользователь нажимает «Готово» на экране инвестиционного счета, и для контроллера выполняется POST, у контроллера теперь есть точное представление базы данных инвестиционного счета, которое необходимо сохранить. Но по какой-то причине я должен загрузить представление домена для внесения изменений, а не просто отображать модель контроллера непосредственно в модель базы данных (модель платформы Entity)?

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

wired_in
источник

Ответы:

9

Хорошо, представьте, что вы реализуете свою страницу создания учетной записи, отображая сообщение формы непосредственно в объект EF, который затем сохраняется в БД.

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

Кажется, все работает нормально. Но тогда бизнес создает новое правило.

  • Счета, созданные в четверг, получают бонусную процентную ставку 2%. (предположим, процентная ставка является одним из полей счета)

Теперь вы должны поместить эту логику куда-то, и у вас нет объекта домена, чтобы вставить его.

DDD предполагает, что у вас всегда будут такие правила, и, вероятно, у вас есть. Создание учетной записи должно иметь различные проверки, журнал аудита и т. Д. Это не будет просто «записать строку в БД»

Планируйте свой домен, предполагая, что в нем нет постоянных или MVC-контроллеров с дополнительной логикой. Убедитесь, что вы собрали все требования, и они все в модели домена.

Ewan
источник
3
Это хороший способ выразить это. Я ненавижу находить бизнес-правила, смешанные с деталями БД. +1
candied_orange
Хорошие моменты, но что, если эти правила проверки применяются только при создании и обновлении пользовательских данных? Затем, когда у нас есть пользовательский ввод, модель, которая создается при выполнении вычислений, является совершенно другой моделью. Должны ли мы иметь две доменные модели для инвестиционного счета? Один для операций CRUD необработанных входных данных для пользователя, а другой - когда эти входные данные используются для создания модели предметной области, используемой в вычислениях?
wired_in
путаю мои вопросы. вам придется привести полный пример. Если у вас есть доменная логика, она должна идти в доменном объекте. Это не означает, что вы не можете создать еще один объект домена из первого
Ewan
Представьте себе сложный расчетный движок. Одним из входных данных, необходимых для выполнения расчетов, является инвестиционный счет, но весь инвестиционный счет для механизма расчета - это поток дохода за некоторый период времени. Эта доменная модель инвестиционного счета полностью отличается от необработанных входных данных, введенных пользователем для этого инвестиционного счета. Тем не менее, когда пользователь вводит основные входные данные, такие как имя, текущее значение и т. Д., Все равно должна быть логика проверки, но она не должна иметь ничего общего с моделью, используемой механизмом вычисления. Так есть ли здесь две доменные модели для инвестиционного счета?
wired_in
..... или, может быть, наличие модели инвестиционного счета в домене является избыточным для операций CRUD, и должны быть только некоторые используемые атрибуты валидатора или что-то в этом роде
wired_in
7

Как это имеет смысл?

Краткий ответ: это не так .

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

Уди Дахан имел интересное наблюдение, которое может помочь прояснить это

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

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

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

Например, большая часть информации, которая появляется в профиле пользователя, например адрес электронной почты или выданный правительством идентификационный номер, имеет источник правды, который живет за пределами вашего бизнеса - это чужой почтовый администратор, который назначает и отзывает адреса электронной почты, а не ваше приложение. Правительство назначает SSN, а не ваше приложение.

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

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

Это верно для случая, когда база данных является книгой рекордов .

Уарзи выразился так .

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

Приложение может считаться CRUD, только если в модели данных отсутствует бизнес-логика. Даже в этом (редком) случае ваша модель данных не является моделью вашего домена. Это просто означает, что, поскольку бизнес-логика не используется, нам не нужна абстракция для управления ею, и поэтому у нас нет доменной модели.

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

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

Итак, может быть, у нас есть два ограниченных контекста здесь? Каждый с другой моделью дляinvestment account

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

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

Если вы считаете, что ограниченные контексты согласованы со службами, может быть проще: сможете ли вы развернуть эти две части функциональности независимо? Да предлагает два ограниченных контекста; но если они должны быть синхронизированы, то, возможно, только один.

VoiceOfUnreason
источник
Хорошо, есть логика проверки и дефолта, но она применяется только при создании / обновлении исходных данных для инвестиционного счета. Затем мы используем гораздо более богатую модель инвестиционного счета, когда используем ее в качестве входных данных для механизма калькуляции. Итак, может быть, у нас есть два ограниченных контекста здесь? У каждого своя модель для инвестиционного счета »
wired_in
Я только что вернулся к этому через несколько лет, и ваш комментарий почему-то резонирует сейчас больше, чем раньше. Здесь много хороших вещей, но не могли бы вы прояснить одну вещь для меня? Вы сказали: «Смысл доменной модели, в конце концов, заключается в том, чтобы гарантировать, что все обновления данных поддерживают текущий бизнес-инвариант». Это относится к той части нашего приложения, которая сохраняет / обновляет информацию. Другая часть - это просто механизм расчета. Он принимает представление данных в качестве входных данных и выдает результаты. Разве это не является частью модели предметной области? Так как это не влияет на хранимые данные?
wired_in
2

В вашем домене вы не должны знать, что база данных вообще существует.

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

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

У дяди Боба есть, что сказать о том, куда поместить ваши данные:

Обычно данные, которые пересекают границы, представляют собой простые структуры данных. Вы можете использовать базовые структуры или простые объекты Data Transfer, если хотите. Или данные могут быть просто аргументами в вызовах функций. Или вы можете упаковать его в hashmap или создать в виде объекта.

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

[…] Когда мы передаем данные через границу, она всегда в форме, наиболее удобной для внутреннего круга.

Чистая Архитектура

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

Шпаргалка чистой архитектуры

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

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

candied_orange
источник
Мы используем ORM (Entity Framework), поэтому наша база данных уже удалена, но модели данных (классы Entity Framework), естественно, в значительной степени 1 к 1 с таблицами базы данных. Проблема в том, что в некоторых частях нашего приложения пользователь, по сути, просто обновляет модель данных (на экране - просто список текстовых полей, где каждое текстовое поле - это поле в базе данных (модель данных).
wired_in
Поэтому я не вижу причины не просто использовать представления необработанных данных (модель данных) при выполнении операций CRUD. У нас есть сложное доменное представление, используемое для вычислений, и это то, что я рассматриваю как нашу модель предметной области, но я не понимаю, почему бы загружать эту картинку в CRUD-часть нашего приложения.
wired_in
Определите, что вы подразумеваете под «использованием представлений необработанных данных». Данные вводятся, данные проверяются в соответствии с правилами домена, данные каким-то образом сохраняются, данные сравниваются, результаты выводятся на что угодно. Я что-то пропустил?
candied_orange
Я пытаюсь сказать, что исходные данные, которые мы получаем от пользователя для инвестиционного счета, не соответствуют тому, как мы представляем этот инвестиционный счет в основных частях нашего приложения, например, когда он используется для расчетов. Например, у нас может быть логический вход, который мы сохраняем в базе данных, который называется IsManagedAccount. Пользователь предоставляет нам это через переключатель на экране редактирования. Таким образом, представление от базы данных до экрана - 1 к 1. Когда мы создадим нашу модель предметной области позже в приложении, у нас может быть класс ManagedAccount, следовательно, нет логического свойства. Две структуры очень разные.
wired_in
Поэтому, когда пользователь просто редактирует необработанные входные данные на экране редактирования, зачем мне загружать изображение домена, а затем добавить большую сложность, чтобы каким-то образом отобразить строго типизированный класс ManagedAccount обратно в плоское представление, представляющее собой всего лишь один класс с IsManagedAccount имущество?
wired_in
1

Применение теории DDD:

В этом домене есть два ограниченных контекста:

  • Расчеты по инвестиционному счету. Математическая модель инвестиционного счета является одним из элементов, возможно, совокупным.
  • Основные финансы. Инвестиционный счет клиента является одним из юридических лиц.

Каждый ограниченный контекст может иметь различный архитектурный дизайн.

Пример:

Клиентский инвестиционный счет является сущностью (может быть совокупной, зависит от домена), и сохранение данных осуществляется через репозиторий сущности (RDB или другой тип БД, например, база данных ОО).

Не существует DDD-подхода к операциям CRUD. Привязка поля БД к данным объекта нарушает принципы проектирования.

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