powered by simpleCommunicator - 2.0.49     © 2025 Programmizd 02
Форумы / Программирование [игнор отключен] [закрыт для гостей] / DDD переосмыслить сознание
19 сообщений из 19, страница 1 из 1
DDD переосмыслить сознание
    #39890044
Фотография X-Cite
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Добрый день, есть некий пример на github Manga: The Clean Architecture Sample Implementation with .NET Core (было просмотрено несколько примеров)

Берем класс Transfer из Manga.Application.UseCases.
Смотрим в Execute

1) Видно что получаем два аккаунта откуда/куда из репозитория.
2) Затем с одного снимаем, на другой кладем.
3) Потом обратно пишем в репозиторий.

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

Вроде бы как с точки зрения размещения бизнес-правил все верно - в одном месте в конкретном объекте.

И тут появляются вопросы:
1) Но если долгов и платежей будет больше 10 - 100 млн. Зачем это все тянуть из хранилища.
2) А если нам не нужна информация о платежах и долгах?
3) Естественно, это некая транзакция (уровня (приложения или БД), (синхронная или асинхронная) не суть) и чем она быстрее завершится - тем лучше. Особенно, если ПО ведет финансовый, а не управленческий учет и тесно связано с минфином, налоговой и т.п, где согласованность данных важна.

И некоторые предположения:
- Конечно, есть вариант хранить промежуточное состояние сумм и получить только их - тогда надо менять бизнес-объект и т.д.
- Можно сделать платежи и долги самостоятельными агрегатами. Тем более в системах уровня ERP на них будут ссылки из других сущностей... Если они будут самостоятельными, где тогда делать проверку на допустимость операции перевода (В агрегате или в usecase или еще где)?
- Также есть вариант, если взять не сильно высоко-нагруженную систему, например ERP-предприятия с онлайном в 200-500 человек. То обычное хранилище в виде РСУБД отлично справится само, посчитав это почти мгновенно.

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

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

P.S. У нас рождается новый проект (И да, мы отлично разбираемся в РСУБД чтобы заставить сервер работать на пределе), который будет интегрирован с другими, и мы бы хотели его реализовать с учетом современных практик реализовав в нем API для доступа к данным, вместо их физической интеграции в другие системы, и при этом есть понимание, что надо изменить собственное сознание для этого. И есть понимание, что кидаться в омут с головой, тоже не есть хорошо.
Возможно исходя из ответов у меня появятся вопросы (скорее уже в новой теме) по CQRS + ES (так как предполагается, если будет API, то читателей будет больше, чем писателей; если же будет интеграция данных, то читателей будет меньше)

Хотелось бы услышать мнение людей по поводу этого примера и моих вопросов, у которых за плечами не один успешный проект.
...
Рейтинг: 0 / 0
DDD переосмыслить сознание
    #39890053
kolobok0
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
X-Cite,

Если прочитать Гради Буча и чётко следовать теории, а не пытаться заюзать только то что знаешь либо кажется проще - то всё банально...

Всё идёт от жизни.
В жизни Вам надо что? КОЛЛЕКЦИЮ, которая отвечает другим глаголам чем одиночный объект. А вот одним из глаголов (выборка-редактирование) будет типо - взять преобразовать такой-то айтем коллекции к такому-то объекту... ну и далее по контексту...

не надо ничего "искусственного". Как мыслите(слышите от спецов в бизнес области) - так и рассуждайте при декомпозиции. Помните что программирование - это автоматизация того, что существует а не открытие чего то нового(я про прикладной слой)

удачи вам, она Вам потребуется к сожалению...

(круглый)
...
Рейтинг: 0 / 0
DDD переосмыслить сознание
    #39891394
Фотография hVostt
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
X-Cite
И тут появляются вопросы:
1) Но если долгов и платежей будет больше 10 - 100 млн. Зачем это все тянуть из хранилища.


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

Поэтому вопрос может быть снят.

X-Cite
2) А если нам не нужна информация о платежах и долгах?


Её и не нужно забирать вместе со счётом, так как она не входит в агрегат. Суть в том, что работать надо с агрегатом, как с цельной структурой.

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


В данном случае это перпендикулярные вещи.


X-Cite
- Конечно, есть вариант хранить промежуточное состояние сумм и получить только их - тогда надо менять бизнес-объект и т.д.


Это про другое.


X-Cite
- Можно сделать платежи и долги самостоятельными агрегатами. Тем более в системах уровня ERP на них будут ссылки из других сущностей... Если они будут самостоятельными, где тогда делать проверку на допустимость операции перевода (В агрегате или в usecase или еще где)?


Нужно.

X-Cite
- Также есть вариант, если взять не сильно высоко-нагруженную систему, например ERP-предприятия с онлайном в 200-500 человек. То обычное хранилище в виде РСУБД отлично справится само, посчитав это почти мгновенно.


Описанное преимущество перестаёт работать в большом спектре современных условий. Также DDD никак не противоречит РСУБД и всем его плюшкам.

X-Cite
во всех легаси-проектах в которых участвовал, логика реализована в БД.


Вам просто очень неповезло :) Логика в БД -- однозначно плохо (ну не считая отдельные вырожденные кейсы, где это действительно подоходит, или вообще необходимо). Замечательно, что вы двигаетесь в правильном направлении.


X-Cite
В приложение, по сути, идут только Query на отображение данных в справочниках и модулях. И если переходить на логику в слой приложения, то в любом случае, та модель данных в хранилище, с которым отлично справляется РСУБД, уже может не подойти для вытягивания данных в слой приложения.


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

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

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

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


Если приходится верстать модель исходя из нефункциональных требований, есть подозрение, что где-то или что-то вы делаете не так. DDD это как раз про бизнес. Каждый агрегат, сущность -- должны ложиться на терминологию бизнеса. Это в идеале, зачастую конечно приходится идти на некоторые корректировки и понимать как конкретно вы собираетесь с данными работать.
...
Рейтинг: 0 / 0
DDD переосмыслить сознание
    #39892068
Фотография X-Cite
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
hVosttРаз уж вы взялиcь за DDD, ознакомьтесь также с CQRS. Возможность делать сколько угодно сложные, хитрые оптимизированные запросы никуда не делась. Просто это решение выражено через абстракцию запросов.

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

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

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

У меня есть еще дыра в размещении некоторой логики над несколькими агрегатами.

Абстрактный кейс из головы:
Перевести клиента в стоп-лист, если его долги превышают платежи.

Есть агрегаты: Клиент, Долги, Платежи. Пусть у клиента будет признак "в стоп-листе". И пусть у нас не будет разноса платежей, чтобы исключить покрытые долги и разнесенные платежи. Будем считать что есть только перечень платежей и долгов.
По сути есть команда, которая содержит только идентификатор клиента.

Варианты:
1) Получается в обработчике этой команды, мне нужен агрегат Клиент, мне нужны коллекции агрегатов Долг и Платеж, которые я получу из соответствующих репозитариев по идентификатору клиента.
Вроде как по логике в агрегате клиента нужен некий метод, куда будут переданы коллекции платежей и долгов. И этот метод проссумировав их и вычислив разницу принял решение о выставлении признака.
2) Нужен только агрегат Клиент, с методом куда будут переданы репозитарии долгов и платежей. Где уже в методе из репозитариев будут получены сразу нужные суммы, которые посчитает хранилище и на основании разницы примет решение о выставлении признака.
3) В самой команде считаем суммы по коллекциям долгов и платежей, или из самих репозитариев получаем нужные цифры, а в метод клиента передаем только цифры, или передаем сразу признак о выставлении, т.е. решение будет принято в команде.
4) Вместо агрегатов долгов и платежей использовать соответствующий квери, результат которого будет использован в агрегате клиента.
...
Рейтинг: 0 / 0
DDD переосмыслить сознание
    #39892384
Фотография X-Cite
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Есть еще один важный вопрос. Например проверка на уникальность...
Если такого значения нет - делаем что-то. Например добавляем сущность в хранилище...
Если бы логика была в РСУБД, то можно сделать так
1) Открываем транзакцию
2)
IF NOT EXISTS(SELECT * FROM TableA AS ta WITH(UPDLOCK) WHERE ta.BBB = @Param)
Делаем INSERT
3) Коммитим транзакцию

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

Как это обычно происходит в CQRS + DDD ?
...
Рейтинг: 0 / 0
DDD переосмыслить сознание
    #39892522
ViPRos
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
X-Cite,

никак
...
Рейтинг: 0 / 0
DDD переосмыслить сознание
    #39892544
Фотография mayton
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
X-Cite, ты слишком много букв написал. Сложно понять главный вопрос или главную идею.

Реально. Постарайся быть кратким. Или как-то акцентировать шрифтом чо собственно надо.
...
Рейтинг: 0 / 0
DDD переосмыслить сознание
    #39892596
Фотография X-Cite
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
mayton,
1) Как поступают с доменной логикой, когда для проверки бизнес-правила требуется множество различных агрегатов в достаточно большом кол-ве экземпляров.

2) Как обычно решается race condition когда одна и та же команда с одними и теми же данными в одно и то же время отправлена на обработку достаточное кол-во раз. Например 50 пользователей одновременно нажали одну и ту же кнопку в одном и том же состоянии, которая запускает одну и ту же команду. (Достаточно ли использовать монитор (lock) на время выполнения команды, чтобы предотвратить race condition).

3) Влиять на конкретный агрегат из разных состояний в другое конкретное состояние (и наоборот) может только одна команда, или несколько? Если несколько, как решается race condition в случае влияния на агрегат в разных командах.
...
Рейтинг: 0 / 0
DDD переосмыслить сознание
    #39892611
Фотография fixxer
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
1) Как правило, такая ситуация признак того, что bounded context порезан неверно. Один юзкейс не должен пересекать границу агрегата.
2) 3) оптимистическая блокировка как правило решает.
...
Рейтинг: 0 / 0
DDD переосмыслить сознание
    #39892627
Фотография X-Cite
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
fixxer,

1) имеется ввиду, что изменяется один агрегат, но для принятия решения нужны другие...
Как пример, я привел 3 агрегата, Клиент, Долг, Платеж. Меняет состояние Клиент, но на основании Долгов и Платежей...
2) 3) Да, спасибо. Так и думал.
...
Рейтинг: 0 / 0
DDD переосмыслить сознание
    #39892630
Фотография hVostt
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
X-Cite
Абстрактный кейс из головы:
Перевести клиента в стоп-лист, если его долги превышают платежи.

Есть агрегаты: Клиент, Долги, Платежи. Пусть у клиента будет признак "в стоп-листе". И пусть у нас не будет разноса платежей, чтобы исключить покрытые долги и разнесенные платежи. Будем считать что есть только перечень платежей и долгов.
По сути есть команда, которая содержит только идентификатор клиента.


В общем, храните вашу логику в сберегательной кассе в сервисах. Команды вносят изменения в систему (изменяют/создают/удаляют), гарантируя корректность и консистентность после изменений.
...
Рейтинг: 0 / 0
DDD переосмыслить сознание
    #39892633
Фотография hVostt
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
X-Cite
Есть еще один важный вопрос. Например проверка на уникальность...
Если такого значения нет - делаем что-то. Например добавляем сущность в хранилище...
Если бы логика была в РСУБД, то можно сделать так
1) Открываем транзакцию
2)
IF NOT EXISTS(SELECT * FROM TableA AS ta WITH(UPDLOCK) WHERE ta.BBB = @Param)
Делаем INSERT
3) Коммитим транзакцию

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

Как это обычно происходит в CQRS + DDD ?


Представьте, что у вас данные расположены сразу в нескольких БД.
При чём, ещё и каких-нибудь разных :)
Как будете решать? Собственно так вы найдёте ответ на свой вопрос.

Не претендую на истину, решать можно как угодно, CQRS/DDD не имеет чётких рецептов на все случаи жизни.
...
Рейтинг: 0 / 0
DDD переосмыслить сознание
    #39892634
Фотография hVostt
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
X-Cite
1) Как поступают с доменной логикой, когда для проверки бизнес-правила требуется множество различных агрегатов в достаточно большом кол-ве экземпляров.


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


X-Cite
2) Как обычно решается race condition когда одна и та же команда с одними и теми же данными в одно и то же время отправлена на обработку достаточное кол-во раз. Например 50 пользователей одновременно нажали одну и ту же кнопку в одном и том же состоянии, которая запускает одну и ту же команду. (Достаточно ли использовать монитор (lock) на время выполнения команды, чтобы предотвратить race condition).


Оптимистичная блокировка, саги, события.
Необходимо абстрагироваться от БД в логике.


X-Cite
3) Влиять на конкретный агрегат из разных состояний в другое конкретное состояние (и наоборот) может только одна команда, или несколько? Если несколько, как решается race condition в случае влияния на агрегат в разных командах.


Версионность агрегатов не позволит вам накатить изменения поверх чужих изменений.

Даже если вы не собираетесь идти в ES, вам всё равно нужна полноценная шина событий, иначе вы не получите должного профита.
...
Рейтинг: 0 / 0
DDD переосмыслить сознание
    #39892635
Фотография hVostt
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
fixxer
1) Как правило, такая ситуация признак того, что bounded context порезан неверно. Один юзкейс не должен пересекать границу агрегата.


+1


fixxer
2) 3) оптимистическая блокировка как правило решает.


+1

:)
...
Рейтинг: 0 / 0
DDD переосмыслить сознание
    #39892904
Фотография X-Cite
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Итак, я понял, что одна команда - позволяет изменить только один агрегат.

Есть довольно интересный пример клиентов и заказов
https://stackoverflow.com/questions/37662541/ddd-do-i-really-need-to-load-all-objects-in-an-aggregate-performance-concerns

И приведен пример:

одна команда

Код: c#
1.
2.
var newOrder = new Order(customerId, ...);
orderRepository.save(newOrder);



вторая команда, как событие после первой

Код: c#
1.
2.
3.
4.
5.
class OrderWasCreatedListener:
    var customer = customerRepository.findOfId(event.customerId);
    var order = orderRepository.findOfId(event.orderId);
    customer.placeOrder(order); //Check business rules
    customerRepository.save(customer);



Я пытаюсь представить что произойдет технически на уровне хранилища при orderRepository.save и при customerRepository.save

Если думать, что хранилищем будет РСУБД, да еще и одна...
Если думать терминами таблица заказов и таблица клиентов... То как бы это совершенно тут не ложится.

т.е. можно предположить о реализации хранения, что при orderRepository.save мы сохраняем заказ, с каким-нибудь начальным признаком, который означает что он еще не отвалидирован.
А при customerRepository.save мы на самом деле не выполняем U для клиента, а на основании информации из placeOrder меняем признак заказа на отвалидирован.
Потому что в границах контекста Создания Заказов - customer и customerRepository это не справочник клиентов...
...
Рейтинг: 0 / 0
DDD переосмыслить сознание
    #39894237
stenford
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
X-Cite
Есть еще один важный вопрос. Например проверка на уникальность...
Если такого значения нет - делаем что-то. Например добавляем сущность в хранилище...
Если бы логика была в РСУБД, то можно сделать так
1) Открываем транзакцию
2)
IF NOT EXISTS(SELECT * FROM TableA AS ta WITH(UPDLOCK) WHERE ta.BBB = @Param)
Делаем INSERT
3) Коммитим транзакцию

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

Как это обычно происходит в CQRS + DDD ?

Никак, DDD не имеет отношения к ограничениям уникальности и не может их валидировать, это в состоянии сделать только сама база, в которой и находятся эти ограничения.
...
Рейтинг: 0 / 0
DDD переосмыслить сознание
    #39894346
Фотография X-Cite
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
stenford,

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

Я к тому, что если есть некий справочник клиентов, со 150 атрибутами, который пользователи могут заполнять в любое время исходя из каких-то своих процессов, то при программировании с применением DDD подхода, в каждом ограниченном контексте класс, который будет представлять клиента, не обязан иметь все эти 150 атрибутов. Теоретически он (класс) вообще может не иметь ни одного атрибута из этого справочника, но иметь другие свойства/атрибуты (расчетные и статические) из других мест, но в данном контексте имеющих отношение к клиенту. И поэтому сколько будет контекстов, столько будет и классов клиента.
...
Рейтинг: 0 / 0
DDD переосмыслить сознание
    #39894773
Фотография hVostt
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
X-Cite
т.е. можно предположить о реализации хранения, что при orderRepository.save мы сохраняем заказ, с каким-нибудь начальным признаком, который означает что он еще не отвалидирован.


нет, команда должна выполняться только на полностью валидных данных, собственно это и позволяет обеспечивать гарантии бизнес-целостности системы при изменениях.
...
Рейтинг: 0 / 0
DDD переосмыслить сознание
    #39895382
Фотография tchingiz
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
mayton
X-Cite, ты слишком много букв написал. Сложно понять главный вопрос или главную идею.

Реально. Постарайся быть кратким. Или как-то акцентировать шрифтом чо собственно надо.

невозможно быть кратким и понятным
начитавшись ГрадиБуча
...
Рейтинг: 0 / 0
19 сообщений из 19, страница 1 из 1
Форумы / Программирование [игнор отключен] [закрыт для гостей] / DDD переосмыслить сознание
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]