Pull to refresh

Comments 4

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

Я правильно понимаю что Вы предлагаете успешно делаете такое: вместо размазывания БЛ по сервисам с невнятной категоризацией (или без неё вообще как на примере с 50тью кейзами) помещать её в методы БЛ объектов(entity)? И что бы БЛ из объектов не лезла во всякие там репозитории - методы возвращают объекты-команды "обновляторы", которые уже и будут применять обновление в БД через репозиторий? Ну т.е. это один из посылов статьи, так? Я только недавно встал на путь "чистого кода", хехе, вопрос наверно наивный, пока для себя расставляю по полкам всё.

Да, и разве SOLID Мартина заставляет гранулировать? В том смысле у него есть понятия скромного объекта, но оно для вещей которые сложно тестировать (типа отрисовки в UI). А вот в какой момент вдруг все БЛ-сущности предлагается хранить отдельно в виде DTO а логику в отдельном месте я вот не помню, но вроде не у Мартина. Перечитаю. Хотя я скорее всего не до конца понял Ваш замысел, так что наверно вопрос глупый, надо перечитать основательно и в сам кукбук поглядеть.

И да, кстати, спасибо за ссылку на ROP, прочистило мозги.

Добрый день, спасибо за обратную связь!

Да, в целом, вы верно поняли мысль.

Вместо размазывания БЛ мы помещаем ее в объекты бизнес логики Entity, Aggregate. В примере исходных кодов я использую подход функциональной мутации имутабильных объектов (DDD made functional), в результате которого на выходе мы получаем запрос на изменение в Aggregate.

Важно обратить внимание на Type Driven Development при дизайне ValueObject-ов — это основной кирпичик + R.O.P. Строим на этом фундаменте вместе с unit тестами.

SOLID Мартина не заставляет, но единая ответственность может быть воспринята неоднозначно, что приводит к чрезмерной единой ответственности — 1 класс : 1 метод.

Запрос на изменение можно отправить-преобразовать в команду — дальше можно развивать историю и там дальше на пути встретимся с ивентами.

Вместо размазывания БЛ мы помещаем ее в объекты бизнес логики Entity, Aggregate

А это видно в примере? Я вот пытаюсь понять в чём и где профит по сравнению с обычное анемичной моделью и до меня не доходит.

Допустим у меня уже есть единый язык и 50 юскейсов. В чём выгода, если юскейсы превратятся в последовательную мутацию промежуточных ДТО (R.O.P имитируем ифами) по сравнению с простым кодом? Я думал, что может в переиспользовании отдельных шагов, но выглядит так, что каждый юскейс под свой конвеер создаёт кучу промежуточных ДТО и они не переиспользуется.

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

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

А это видно в примере? Я вот пытаюсь понять в чём и где профит по >сравнению с обычное анемичной моделью и до меня не доходит.

Чтобы ответить на ваш вопрос я приведу некоторые недостатки Слабой доменной модели:
- Инкапсуляция нарушена
- Приводит всегда к Дублированию бизнес-логики
- Невозможно гарантировать, что объекты в модели находятся в согласованном состоянии
- всегда способствует разрыву и непониманию между разработкой и бизнесом

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

чем плохо слияние с интеграцией?

- тем что тестирование бизнес-логики возможно только моками

Также рекомендую прочитать про этот анти-паттерн у Martin Fowler -> https://www.martinfowler.com/bliki/AnemicDomainModel.html

Преимущества Сильной доменной модели по Type Driven:

- бизнес-логика ограничений и поведения собрана в одном месте 

   - легко поддерживать, развивать, изучать

- Эта логика направляет и контролирует создание, проверку и управление сущностью, предотвращая появление у клиента сущностей с несогласованным состоянием сразу в одном месте

- код превращается в документацию

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

- в самом ядре бизнес-логика инвариантов описана по паттерну ValueObject's, например: SubscriberId - сразу контролирует свое возникновение и описывает ограничения на свое появление.

https://git.codemonsters.team/guides/ddd-code-toolkit/src/branch/dev/src/main/kotlin/team/codemonsters/ddd/toolkit/domain/subscriberDataUpdate/SubscriberId.kt

https://git.codemonsters.team/guides/ddd-code-toolkit/src/branch/dev/src/test/kotlin/team/codemonsters/ddd/toolkit/domain/subscriberDataUpdate/SubscriberIdTest.kt

- такое ядро максимально удобно тестировать юнит тестами - тестируем выходные данные функции -  а это самый эффективный тест

Допустим у меня уже есть единый язык и 50 юскейсов. 

50 - много. стоит поработать с ограниченным контекстом и декомпозировать историю


В чём выгода, если юскейсы превратятся в последовательную мутацию промежуточных ДТО (R.O.P имитируем ифами) по сравнению с простым кодом? 

пайплайн ориентированный подход нам помогает решать такие проблемы:

- код есть документация - просто читать не только программисту

- это всегда однонаправленный поток, даже с ответвлениями

- упрощает компоновку шагов

- помогает следовать принципам хорошего дизайна, заворачивает нас в создание функций с 1,2 входными параметрами- проще мэйнтэйнить

- проще тестировать 

- вот классное видео про П.О.П.
https://www.youtube.com/watch?v=sfYA0HCWgq - в кукбуке я его также оставил.

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

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

- можно переиспользовать шаги, мы так делаем

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

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

Например:
https://git.codemonsters.team/guides/ddd-code-toolkit/src/branch/dev/src/main/kotlin/team/codemonsters/ddd/toolkit/domain/subscriberDataUpdate/SubscriberDataUpdate.kt#L3
мы работает с обновлением абонента и данный класс Aggregate исполняет свою цель - порождает запрос на обновление абонента, который передается по пайпу далее в точку входа изменения абонента.
Точка входа изменения абонента - сама по себе может быть как простой задачей, так и сложной - можно передать в DataBase Adapter или запустить процесс обновления в сервисе subscribers.

Т.е. пример приложения в целом покрывает умную update crUd операцию.

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

само по себе обновление сущности абонента уже более сложное: 
тут вызов

https://git.codemonsters.team/guides/ddd-code-toolkit/src/branch/dev/src/main/kotlin/team/codemonsters/ddd/toolkit/domain/subscriberDataUpdate/SubscriberDataUpdateService.kt#L61

вызывается сервис subscribers

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

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

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

Надеюсь я ответил на ваши вопросы, если что-то не понятно - пишите.

Sign up to leave a comment.