Pull to refresh

Comments 46

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

Был когда-то в аналогичной ситуации. Ещё через 5 лет после этого понял, что DDD это красивая сказка. Я много раз пытался сделать «как надо», а также много раз видел как другие пытаются. Всегда получается та самая ситуация, когда вместе с бананом прилетает ещё и горилла с джунглями.


Плясать нужно от фреймворка и библиотек. Это не повод писать лапшу, конечно. Но идея о том, что можно сделать абстрактную модель, а потом отобразить ее на имеющуюся инфраструктуру очень наивна. Кто сказал, что это вообще возможно эффективно сделать? Видели сколько кода в ORM? Оно отображает объектную модель в реляционную. Почему кто-то считает, что задача отображения доменной модели на имеющуюся инфраструктуру (БД, API, кеши, очереди и т.п.) чем-то проще?

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

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


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

Дело в том, что сложность ORM — это особенность исключительно реляционных субд.


Я смотрел, конкретно в OrientDB идентификатор записи — это индекс в grow-only массиве, в котором находятся смещения в файле данных. То есть любой переход по ссылке — это 2 хопа, а не в лучшем случае O(log) как в реляционых.

Дело в том, что сложность ORM — это особенность исключительно реляционных субд.


нет, сложность ORM это
— query object или своя язык запросов
— unit of work и change tracking
— relationships, lazy loading
— управление соединениями
— и в последнюю очередь мапинг того что достали из базы, на объекты

То есть любой переход по ссылке — это 2 хопа, а не в лучшем случае O(log) как в реляционых.


Кажется hash index даст те же 2 хопа для реляционной базы.
query object или своя язык запросов

Тут нет ничего сложного в случае графовой субд.


unit of work и change tracking

Тут в принципе тоже.


relationships

В графовой субд это тривиальная штука.


lazy loading

Это не нужно, если есть fetch-plan.


управление соединениями

ORM-то ту при чём?


Кажется hash index даст те же 2 хопа для реляционной базы.

Ресайз хеш-таблицы — дорогое удовольствие. А если не ресайзить — будет больше 2 хопов.

идея о том, что можно сделать абстрактную модель, а потом отобразить ее на имеющуюся инфраструктуру очень наивна. Кто сказал, что это вообще возможно эффективно сделать?

Да доказательств никаких не было и нет. Просто кому-то когда-то это показалось возможным и пошло поехало…
По мне так верно обратное. Инфраструктура в основном тон проекту задает, особенно нагруженному. Разработчики получают деньги как раз за «укрощение инфраструктуры». В последнее время пришел к выводу, что более эффективными являются абстракции построенные «снизу» от инфраструктуры, а не сверху по описанию предметной области.
Я думаю, что скорее не «DDD — это красивая сказка», а «Бескомпромиссное DDD зачастую создает больше проблем, чем решает».

Но идея о том, что можно сделать абстрактную модель, а потом отобразить ее на имеющуюся инфраструктуру очень наивна.

Строго говоря, все языки высокого уровня — это «абстрактная модель, отображающася на машинный код». Процессор же не работает с вашей C# программой напрямую.

Плясать нужно от фреймворка и библиотек. Это не повод писать лапшу, конечно.

А как вы предлагаете бороться со сложностю в бизнес-приложениях?
Строго говоря, все языки высокого уровня — это «абстрактная модель, отображающася на машинный код». Процессор же не работает с вашей C# программой напрямую.

То что мы язык программирования отобразили в машинный код ещё не означает, что модель, построенная по правилам DDD будет отображаться на вашу инфраструктуру и фреймворки. В самом DDD никакой аргументации «за» тоже нет. Этот вопрос просто обходиться стороной.


А как вы предлагаете бороться со сложностю в бизнес-приложениях?

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

UFO just landed and posted this here
В самом DDD никакой аргументации «за» тоже нет. Этот вопрос просто обходиться стороной.

Это не так. У Эванса нет отдельной главы «зачем это вам нужно и что будет если этого не делать», но при желании можно найти аргументацию «за». У Вернона даже есть глава «Why should I use DDD» (или как-то вроде того).

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

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

Ровно как и нет обоснований тому что «ООП» лучше или хуже «ФП» или «процедурного программирования» или того что монолит лучше/хуже микросервисов, Angular — React'а или Vue. Есть сильные и слабые стороны, причем в разных контекстах одно и тоже свойство может быть сильной стороной, а в другой — слабой. Скажем, внутреннее изменяемое состояние объектов — главый «козырь» ООП против процедурной парадигмы в многопоточной среде выходит боком.

Просто сказано примерно следующее — ваш код говно потому что вы не следовали заповедям.

Я такого не видел. «Ваш код сложно поддерживать, сложно/долго/дорого ввести нового разработчика в команду» видел.
bm13kk VolCh отвечу тут разом всем, в основном про «с чего вдруг DDD помогает бороться со сложностью?»

Вы говорите, что DDD упрощает жизнь каким-то особенным, только ему присущим способом и это делает его ценным. Упоминается слияние с бизнес-языком, отсутствие роли переводчика и так далее. Хочу обратить ваше внимание вот на что. До DDD был MDA (Model Driven Architecture), а также были популярны DSL (Domain Specific Languages) — они тоже декларировали описание проблемы на языке максимально близком к бизнесу.

На заре ООП, еще в 80х, ему приписывали такое свойство
Many people who have no idea how a computer works find the idea of object-oriented programming quite natural.

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

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

Кроме того есть еще конкурирующие подходы типа RAD. Аргументированного сравнения DDD и RAD я никогда не видел, но с удовольствием бы почитал.

Итого
— Идея приближения к бизнес языку была еще в 80х.
— Практически каждый подход к проектированию утверждает, что он позволяет приблизиться к бизнес языку.
— DDD не поясняет почему их метод вообще работает и чем он лучше сотен других.
— Есть альтернативы, которые могут быть в 100 раз лучше или хуже.

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

PS: Можно еще задасть вопросом «а что такое сложность» и попытаться это понятие формализовать. Двигаясь по этому пути легко уйти в абстрактную алгебру и категории. Но тогда вопросы «помогает ли DDD» отпадут сами собой.
UFO just landed and posted this here
а с чего вдруг DDD помогает бороться со сложностью? Никакого теоретического обоснования этому нет.

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

То же самое говорили про ООП, RAD и DSL. А теперь есть еще движение No-code, там вообще предлагают (в который раз) программистов заменить на конструкторы приложений. То есть проблема с пониманием предметной области есть. Решений предлагалось и предлагается много. Все они плохие.

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

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

— Asynchronous injection
Проблема не решилась. Бизнес правило приходит снаружи конструктора/фабричного метода. Я как нехороший программист обязательно положу в туда бяку.

— Internal for infra
В одной сборке с бизнес логикой будет валяться инфра код. Не чисто.

Тема DDD на .netnext'е была освещена слабо. Доп. доклад Data DD vs Domain DD был вообще терменологически, по содержанию не корректным. Воркшоп тоже прошёл не на ура. Данный доклад был красивый, с парой очень метких выводов, которые я взял на вооружение, но всё равно местами спорный. Спорный в том плане, что всё таки следует разводить CRUD приложения и DDD.


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


Не очень хороший пример над большими данными, особенно для отчётов и запросов (cRud). Вы сами пишете о CQS&CQRS — очевидно что модели для команд, модели для запросов — фанатизм.


Не соглашусь с вашим тезисом о дороговизне. Вы включили такие составляющие (уточнял после доклада):


  1. Формирование единого языка;
  2. первоначальная реализация;
  3. глубокий и непрерывный рефакторинг;
  4. первоначальное построение инфраструктуры.
    Для 1 и 3 важные части актуализации модели — без них DDD нет — это обязательные затраты.
    2 и 4 могут копироваться по шаблону, и в реальности многие используют примеры MS.

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


Данная статья лучше самого доклада — хороший материал для ликбеза.




P.S. Несмотря на обещанное в комментарии, на вентилятор накинуть ни капли не получалось

Спорный в том плане, что всё таки следует разводить CRUD приложения и DDD.

Мне казалось, что на оси координат в пункте «Что выбрать» я и предлагал «разводить»

Не слишком ли вы склонны смешивать модель и инфраструктуру? Это же ограничивает реализацию, по известному закону в самый неподходящий момент

Нам показалось это меньшим злом.

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

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

А сколько итераций рефакторингу модели максимум вам приходилось пройти на основе этой архитектуры?


И ещё лучше вопрос — если бы вы делали не продукт под конкретного заказчика, а масштабный проект, не хотели бы вы архитектуру хардкорнее анемичной модели, в каком-то плане чище?

А сколько итераций рефакторингу модели максимум вам приходилось пройти на основе этой архитектуры?

В больших системах у нас обычно бывает 1-2 неприятные ошибки, но не в архитектуре как таковой, а в структуре БД и понимании взаимоотношений one2one / one2many / many2many.

И ещё лучше вопрос — если бы вы делали не продукт под конкретного заказчика, а масштабный проект, не хотели бы вы архитектуру хардкорнее, в каком-то плане чище?

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

Не совсем имел ввиду ошибки. У меня с коллегами по работе был опыт 7 итераций рефакторинга в одном контексте. В одних мы уточняли ЕЯ, модель, спецификации, другие носили технический характер, где мы как раз разводили инфраструктуру и модель.


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




Вот еще два (раз, два), полагаю, технических специалиста

Такое бывает. Не все могут усвоить эти практики. Кому-то они далеки методологически, кто-то не понимает посыла автора, кто-то понимает слишком буквально. Сам подход применим не в любых задачах, не для любых команд. Посмотрите видео с ArchDays — многие докладчики говорили о том что применяют DDD, хоть и упоминают осторожно, но у них это работает.




Мы делали как кулуарные проекты под конкретных заказчиков так и проекты федерального уровня.

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


Вот в этом случае, хотели бы вы делать насыщенные модели?

Вот в этом случае, хотели бы вы делать насыщенные модели

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

Ссылки не работают.


Шпаргалка по основам DDD

404

А я поправил в статье, теперь везде правильно.
UFO just landed and posted this here
Отличная статья, спасибо, однозначно закладочку
В анемичной, все ровно наоборот.
1. Структуры данных и операции над ними (поведение) разделены. Чаще всего структуры данных называют «сущностями», а поведение — «сервисами».
2. Структура программного код отражает скорее паттерны и фреймворки.
Бизнес-правила либо невозможно понять, путем изучения кода, либо это дается с большим трудом.
3. И наконец, инварианты не соблюдаются, а зачастую даже отрицается такая необходимость или возможность

Не вижу почему за пунктом 1 обязательно должен следовать 2 и 3 и почему теми же недостатками 2 и 3 не может обладать богатая модель.
Каждый из этих самых «сервисов» или «менеджеров» в анемичной модели может быть предназначен для решения определенной части общей доменной задачи. Смысл задачи одного менеджера может не иметь прямого аналога в задачах домена, но часто очень близок к ним. Это менеджеры для отслеживания и обработки каких-то событий домена, выполнения каких-то задач анализа и моделирования.
Структура кода — это и есть набор этих менеджеров или сервисов, каждый из которых ответственен за какую-то часть поведения системы. Бизнес-правила очень легко понять изучая алгоритмы в сервисах. Непротиворечивость состояний объектов гарантируется алгоритмами сервисов, которые не делают ничего неправильного.
И наоборот — богатая модель может обладать всеми этими недостатками. Код разбросан по доменным объектам и они порождают общее поведение системы, которое очень сложно понять из кода отдельных объектов и сопоставить с бизнес-правилами (все главное всегда происходит где-то в другом месте и нигде).
Каждый объект думает только о себе и понять для каждой ситуации не станет ли их состояние противоречивым крайне сложно, и так далее.
Не вижу почему за пунктом 1 обязательно должен следовать 2 и 3 и почему теми же недостатками 2 и 3 не может обладать богатая модель.

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

Каждый из этих самых «сервисов» или «менеджеров» в анемичной модели может быть предназначен для решения определенной части общей доменной задачи. Смысл задачи одного менеджера может не иметь прямого аналога в задачах домена, но часто очень близок к ним. Это менеджеры для отслеживания и обработки каких-то событий домена, выполнения каких-то задач анализа и моделирования.

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

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

А потом кто-то в команде создает «неправильный объект» и система переходит в несогласованное поведение.

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

Написать плохой код гораздо проще, чем хороший в любом случае. Конечно вы правы. Далее по тексту статьи есть мысли о том, что в функциональных языках DDD делать в чем-то проще из-за:

  • более выразительной системы типов
  • разделения функций и объектов


При этом не надо спорить про «богатую» или «анемичную», потому что сам вопрос лишен смысла другими начальными установками языка. Объекты в ФП принято делать неизменяемыми, поэтому риск случайного изменения объекта ниже, а из-за более краткого синтаксиса объектов и discriminated union проще соблюдать инварианты. Т.е. для меня вопрос лежит не в плоскости черного и белого, а скорее в пятидесяти оттенках серого.
> риск случайного изменения объекта ниже
Разве такое бывает? Примеры есть? Я понимаю, что можно случайно удалить базу данных на продакшене вместо тестовой копии, а когда изменяется объект не по алгоритму, то это ошибка, и ее надо исправить, не так ли?
а когда изменяется объект не по алгоритму, то это ошибка, и ее надо исправить, не так ли?

А вам больше нравится исправлять ошибки или не допускать? Исправлениям ошибок стоит денег. Чем позже ошибка найдена, тем дороже. Конечно для предотвращения ошибок можно и в ряде случаев нужно писать тесты. Но тесты тоже стоят денег. Здесь уже каждый сам решает, какой метод работает лучше в команде.
Не обязательно, но довольно часто, потому что чаще всего при использовании анемичной модели не соблюдаются инварианты.
Перечитав все еще раз начал понимать одну возможно очевидную для всех вещь. Все что говорится о DDD говорится применительно к особому типу задач: это корпоративные приложения для учета движения-оформления чего-то, документооборот, некие финансовые транзакции, интернет магазины и прочее в этом роде. Причем, при условии создания этих систем множеством программистов, каждый из которых не должен иметь возможность все поломать.
Этот круг задач занимает львиную долю рынка задач разработки и наверно человеку, который в ней вырос кажется, что это и есть почти все программирование.
Но, например, мне ни разу не пришлось делать ничего подобного.
Когда я читаю про DDD, то пытаюсь приложить это к системам проектирования, моделирования неких технических объектов, системам выбора решения, визуализации неких данных, прикладным графическим редакторам, сбору данных с телеметрии и прочим задачам, в которых есть доменная модель, но для которых концепции DDD не адаптированы вообще никак и абсолютно не помогают победить сложность.
Фаулер по-моему забыл явным образом упомянуть этот факт. Поэтому, когда-то его книга про DDD оставила меня в смутном недоумении. Бывают такие недоразумения.
Ага. Системы, которые вы описали у меня в секторе «технически сложные, но с понятной бизнес-логикой» или «технические сложные, со сложной бизнес-логикой». Там DDD не дает выигрыша.

Возможно, мне одному кажется, что вот эта "Богатая" модель это всего лишь вариация на тему active record, со всеми известными его недостатками. Лично я нахожу, что чем более тупые объекты передаются в качестве параметров-результатов, тем проще всем жить. Корректные инварианты могут выражаться в типах и функциях-парсерах, конвертирующих более общий тип в менее общий. И тестировать их куда проще, а для "богатых" моделей у нас помню на одной работе был целый тестовый фреймворк, чтобы создавать сущности в валидном состоянии, потому что там шаг влево шаг вправо — и где-нибудь вылезет эксепшн, даже если тебе в рамках конкретного теста вообще плевать, что там внутри.

Все-таки есть разница. Active Record подразумевает наличие метода Save прямо в сущности, что сразу мешает в один суп аспекты хранения и бизнес-логику. Сущности в Domain Model часто конструируются с помощью ORM, но это не обязательное условие. Отсюда и сложности с агрегатами: приходится тащить в память весь граф объектов. Был бы классический ActiveRecord — тащили бы из БД и горя не знали.

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


А как в "богатой" модели такое сделать у меня сходу ответа даже не найдется. Т.е. если у нас не анемичная доменная модель, то заказ знает сам как себя подтвердить, так? И вот весь вопрос, как сделать массовое потверждение заказов?

Отличный вопрос. Даже два.

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

Здесь все не так просто и однозначно. Если вы используете ORM и хотите именно параллельные запросы на сохранение, то OrderParallelService будет каким-то таким

public class OrderParallelService(Func<DbContext> dbContextCreator)
//...
public async Task SaveParallel(IEnumerable<Order> orders)
{
    var tasks = orders.Select(async x => {
        var ctx = _dbContextCreator.Create();
        ctx.Add(x);
        await cts.SaveAsync();
    })

    return Task.WhenAll(tasks);
}

На мой вкус здесь вопрос лайфтайма UoW, а не богатой / бедной модели. Т.е. что в богатой, что в анемичной код может быть таким:

public async Task<IActionResult>([FromServices] IServiceProvider sp, CreateOrders command)
{
    await Task.WhenAll(command.OrderDtos
        .Select(async x => sp.InScope<IUnitOfWork>(uow => {
            uow.Add(new Order(x));
            return uow.CommitAsync();
    }))
}

По этому поводу хорошо у Симана написано. Асинхронность и лайфтаймы все-равно появятся. С практической точки зрения не так важно в контроллере этот код или в сервисе. В контроллере даже чем-то лучше.

Либо вы имели в виду другое.

public async Task SaveParallel(IEnumerable<Order> orders)
{
   _sql.Invoke($"INSERT INTO Orders (...) Values ({orders.GetValues()})");
}

Тогда тоже сервис будет одинаковым как в случае реализации анемичной, так и богатой. Либо с использованием Bulk Extensions:

public async Task SaveParallel(IEnumerable<Order> orders)
{
   context.BulkInsert(entitiesList);
}

И вот весь вопрос, как сделать массовое потверждение заказов

С помощью Batch Extensions

context.Orders
    .Where(a => a.Id <= 500)
    // тут есть проблема с доступом к конструктору и приватным полям
    // но Expression можно объявить как статическую переменной класса
    // и будет норм
    .BatchUpdate(a => new Order { State = OrderState.Confirmed });

или написав SQL-запрос:)

Вообще если реализовывать то наверное как-то так:


public class OrderParallelService(DbContext dbContext)
//...
public async Task SaveParallel(IEnumerable<Order> orders)
{
    var ordersToSave = DoSomethingWithOrders(orders);
    _dbContext.Orders.AddRange(ordersToSave);
    await _dbContext.SaveChangesAsync();
}

А все вопросы про время жизни DbContext это не моя зона ответственности — пусть тот кто меня создает (как правило — DI) и думает, как мне инстанс передать, потому что моему классу это по-барабану, его дело заказы сохранять, а не коннектами в БД заведовать.


Далее


На мой вкус здесь вопрос лайфтайма UoW, а не богатой / бедной модели. Т.е. что в богатой, что в анемичной код может быть таким:

Простите, в анемичной модели Order явно такого метода не будет, потому что для этого есть сервисы. А в богатом случае у вас класс единственного заказа почему-то начал получать массив OrderDtos, что как-то вразрез с его логикой идёт, нет? Какой смысл в методе SaveParallel у одного заказа?


Как совать в БД: через сохранение в контексте или сырой SQL совершенно неважно (хотя, очевидно, стоит пользоваться преимущствами ORM).




В итоге, кмк вопрос остался без ответа: как в богатой модели будет выглядеть такой метод. Если и правда class Order получает такой метод, то это выглядит странно, потому что сохранение массива заказов не ответственность одного заказа.

Как-то мы про разное пишем. Что в «богатой», что в «анемичной» бывают сервисы, просто в «анемичной» в сервисах все, а в «богатой» — то, что не полезно в сущности и агрегаты. Вот в этой статье есть неплохой разбор.
Спасибо за отличный пост!
Глобально кмк, у вас не раскрыта тема «а что должен сделать, скажем information architect, чтобы не пришлось разгребать огрехи модели на уровне системной архитектуры». Кмк, очевидно, что каждый второй проектировщик физической модели будет вам горриллой по банану бить, если на концептуальном, а лучше и логическом уровне ему не донесли прозрачно взаимосвязь сущностей и линков. Я подозреваю, что тот же г-н Фаулер предполагает, что перед непосредственно разработкой над задачей поработали (или хотя бы благословили) адекватные энтерпрайз, information и солюшн.
Немного деталей — не уловил, как после 2:0 счет внезапно стал 2:2.
И почему cRud для вас проблема? В чем постыдность запроса данных не по заданному контракту? Если физ модель не стыкуется с логической, а расползания не хочется допустить — можно сверху натянуть а-ля graphql с приведением к нужной объектовке. А вот с CrUD этот фокус уже не прокатит, да.
Глобально кмк, у вас не раскрыта тема «а что должен сделать, скажем information architect, чтобы не пришлось разгребать огрехи модели

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

Немного деталей — не уловил, как после 2:0 счет внезапно стал 2:2.
Не везет этому счету: во время доклада нечетко объяснил принцип ведения, во время расшифровки забыли вставить присуждение очков в паре мест. Текст исправлен, теперь все присуждения очков на месте.

И почему cRud для вас проблема? В чем постыдность запроса данных не по заданному контракту?

Для меня не проблема. Скорее, наоборот, все понятно — делаешь отдельный стек под чтение и горя не знаешь.

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

На сколько я понимаю, OData появился путем подобных рассуждений, но что-то не зашло. Может быть у graphql получится лучше. Технология еще молодая, поживем — увидим.
Что касается проблемы получения из базы слишком большого числа объектов, использовал следующее рабоче-крестьянское решение разрешенного, но запрещенного ленивого чтения.
Во-первых задаемся вопросом: почему объекты это лучше чем запросы SQL? Потому что так код получается гораздо понятнее и проще. Этот код работает с доменными объектами так, как будто вообще весь доменный мир объектов в его распоряжении.
Но на самом деле это не так — все объекты не могут находиться в оперативной памяти.
Тогда я применил следующее решение. Включил ленивое чтение и добавил счетчик ленивых чтений. Дальше, в начале каждой процедуры, которой нужно много объектов для работы дописываем один или несколько запросов к ORM, подымающих все объекты, которые дальше потребуются в память. В конце выполнения процедуры проверяется счетчик ленивых чтений и выкидывается сообщение в лог, если он не 0.
Тогда разработка происходит так:
1. Тупо пишем алгоритм так, как будто весь мир уже в оперативке, полагаясь на ленивое чтение. Т.е. преимущество работы с доменными объектами используется по максимуму.
2. Когда все заработало, дописываем требуемые запросы к базе, подымающие в память нужные данные до начала работы алгоритма анализа и добиваемся полного отсутствие в логе сообщений о ленивых чтениях. Здесь же решается проблема возможной несогласованности данных при ленивом чтении.
Положительным моментом является то, что задача 1 абсолютно не засоряется проблемами задачи 2 и может быть написана разработчиком прикладного направления. Задача 2 решается исходя из понимания какие данные нужны задаче 1, но по сути может делаться другим программистом и является чисто оптимизационной. При этом алгоритм задачи 2 может быть сколь угодно сложным, даже почти повторять алгоритм задачи 1 (если не удается все достать одним запросом), но все равно при готовом алгоритме 1 его по сути напишет и обезьяна.
Спасибо большое за доклад! Хочу дополнить вопросом и рассказом,

Вопрос: Если мы имеем и используем сильную декларативную выразительность ограничений типов, это еще лишь точная Анемичная модель, или уже недоделанная Богатая?

Рассказ: Когда речь заходит про выразительность модели предметной области, то мне сразу вспоминается Онтологическое проектирование вместе с инструментами RDFS и OWL, при знакомстве с которыми я был удивлен насколько иначе можно представить предметную область, нежели в известных мне C#/Java/RDBS.

Как пример, Наследование Классов:
В Онтологиях наследование «subClassOf» означает сужение, уточнение подмножеств классов и экземпляров, что соответствует действительности, ведь Студентов вообще меньше чем Человеков.
В Классических ООП языках наследованием же обычно называется расширение базового класса. Наблюдательный программист увидит в этом конкретизацию типа (ведь Student унаследованный от Person можно привести к Person, но не наоборот), особо заметно это на примитивах.
Однако, в обсуждениях зачастую я слышу именно термин «расширение».

Другой пример, Наследование Свойств:
Если свойство «Length» имеет наследников «OfficialLength» и «EstimatedLength», то они унаследуют ограничения базового свойства, а также, наличие любого из свойств наследников будет означать наличие базового.
Заголовок спойлера
image

В классическом ООП и реляционных БД обычно такое реализуются через связующую сущность (таблицу) с необходимыми свойствами, и хранением типа этой сущности, справедливости ради это позволяет сохранить даже больше информации о связи.
Однако, создаются такие промежуточные сущности тоже нечасто (для этого требуются манипуляции в хранилище) и зачастую «протекают» (если их вовремя не превращают в самостоятельную сущность). 99% свойств это всё таки именно свойства.

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

К сожалению не подскажу за другие языки программирования, знаю лишь поверхностно, интересно было бы узнать как там с подобными понятиями.
В Онтологиях наследование «subClassOf» означает сужение [...] Однако, в обсуждениях зачастую я слышу именно термин «расширение».

Это связанные явления. При сужении множества объектов всегда расширяется множество их общих свойств, и наоборот.


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

Sign up to leave a comment.