Добрый день! Благодарю вас за хороший вопрос. Простите за долгую реакцию - в связи с высокой загрузкой у меня нет возможности своевременно отвечать на вопросы. Для этого у меня есть утро Субботы или Воскресенья.
А это видно в примере? Я вот пытаюсь понять в чём и где профит по >сравнению с обычное анемичной моделью и до меня не доходит.
Чтобы ответить на ваш вопрос я приведу некоторые недостатки Слабой доменной модели: - Инкапсуляция нарушена - Приводит всегда к Дублированию бизнес-логики - Невозможно гарантировать, что объекты в модели находятся в согласованном состоянии - всегда способствует разрыву и непониманию между разработкой и бизнесом
- всегда приводит к описанию бизнес-логики в отдельном месте, например сервисе и сливается в этом случае с интеграцией.
чем плохо слияние с интеграцией?
- тем что тестирование бизнес-логики возможно только моками
Преимущества Сильной доменной модели по Type Driven:
- бизнес-логика ограничений и поведения собрана в одном месте
- легко поддерживать, развивать, изучать
- Эта логика направляет и контролирует создание, проверку и управление сущностью, предотвращая появление у клиента сущностей с несогласованным состоянием сразу в одном месте
- код превращается в документацию
- по кукбуку - бизнес-логика - отделена от интеграций - ее просто тестировать, а сервисный слой - тупая труба - его тестировать одним интеграционным тестом. Снижаем количество интеграционных тестов - меньше кода и тестовых конфигураций нужно писать.
- в самом ядре бизнес-логика инвариантов описана по паттерну ValueObject's, например: SubscriberId - сразу контролирует свое возникновение и описывает ограничения на свое появление.
- в него проще погружать новичков и следить за и тем, как они развивают историю
Я думал, что может в переиспользовании отдельных шагов, но выглядит так, что каждый юскейс под свой конвеер создаёт кучу промежуточных ДТО и они не переиспользуется.
- можно переиспользовать шаги, мы так делаем
Но главная мысль - каждый бизнес-процесс, как флоу - независимая история и мы описываем его здесь и сейчас не думаем о других процессах. Если у нас в сервисе много разных бизнес-процессов - это сигнал задуматься и разнести бизнес-логику, чтобы упростить систему. Если в компонентах есть какие-то общие шаги - их можно переиспользовать.
Хотелось бы видеть реализацию какой-нибудь CRUD задачи, когда нужно >обновить некую сущность в соответствии с какими-нибудь бизнес правилами.
Т.е. пример приложения в целом покрывает умную update crUd операцию.
Например: при создании заказа нужно проверить, что средств хватает, сохранить выбранные строчки корзины в заготовку заказа, поставить в очередь задачу на отправку письма о созданом заказе, поставить в очередь задачу на дальнейшую обработку заготовки заказа, удалить строки из корзины.
само по себе обновление сущности абонента уже более сложное: тут вызов
где запускается процесс обновления абонента по бизнес-правилам и там несколько сущностей обновить нужно, одну добавить.
Что самое важное: код там подобен примеру, просто чуть больше шагов и есть развилки. Я не стал специально усложнять этот пример - показал основные кирпичики. Если инженер начнет экспериментировать с этой историей - он уже на конкретном примере начнет ощущать, на сколько она хороша.
В дальшейшем я планирую запустить один эксперимент, который наглядно демонстрирует насколько эта идея круто работает от фронта до бэка. Если вам будет интересно следить за обновлениями по теме - я скину линк на телеграм канал.
Надеюсь я ответил на ваши вопросы, если что-то не понятно - пишите.
Вместо размазывания БЛ мы помещаем ее в объекты бизнес логики Entity, Aggregate. В примере исходных кодов я использую подход функциональной мутации имутабильных объектов (DDD made functional), в результате которого на выходе мы получаем запрос на изменение в Aggregate.
Важно обратить внимание на Type Driven Development при дизайне ValueObject-ов — это основной кирпичик + R.O.P. Строим на этом фундаменте вместе с unit тестами.
SOLID Мартина не заставляет, но единая ответственность может быть воспринята неоднозначно, что приводит к чрезмерной единой ответственности — 1 класс : 1 метод.
Запрос на изменение можно отправить-преобразовать в команду — дальше можно развивать историю и там дальше на пути встретимся с ивентами.
Я сделаю статью с примером по кукбуку - из исходников много можно будет чего вынести. Также сделаю более подробные остановки на каждом пункте кукбука. В JPoint - была проба пера и я после этой записи много улучшений привнес. Благодарю за обратную связь!
да, так и планирую. Каждый раздел - целая вселенная. Следующая будет уже скоро и содержать больше подробностей. К сожалению, общеизветные правила не все применяют и не применяют по делу
Кайф, Ваня. Спасибо!
Умеешь ты развлекаться!
Хотел все Linux поставить на планш, ты напомнил. +1 к моим развлечениям )
Спасибо за статью, хорошо и понятно описывает базовые принципы!
Буду рекомендовать падаванам!
Добрый день! Благодарю вас за хороший вопрос.
Простите за долгую реакцию - в связи с высокой загрузкой у меня нет возможности своевременно отвечать на вопросы. Для этого у меня есть утро Субботы или Воскресенья.
Чтобы ответить на ваш вопрос я приведу некоторые недостатки Слабой доменной модели:
- Инкапсуляция нарушена
- Приводит всегда к Дублированию бизнес-логики
- Невозможно гарантировать, что объекты в модели находятся в согласованном состоянии
- всегда способствует разрыву и непониманию между разработкой и бизнесом
- всегда приводит к описанию бизнес-логики в отдельном месте, например сервисе и сливается в этом случае с интеграцией.
чем плохо слияние с интеграцией?
- тем что тестирование бизнес-логики возможно только моками
Также рекомендую прочитать про этот анти-паттерн у 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 - много. стоит поработать с ограниченным контекстом и декомпозировать историю
пайплайн ориентированный подход нам помогает решать такие проблемы:
- код есть документация - просто читать не только программисту
- это всегда однонаправленный поток, даже с ответвлениями
- упрощает компоновку шагов
- помогает следовать принципам хорошего дизайна, заворачивает нас в создание функций с 1,2 входными параметрами- проще мэйнтэйнить
- проще тестировать
- вот классное видео про П.О.П.
https://www.youtube.com/watch?v=sfYA0HCWgq - в кукбуке я его также оставил.
- в него проще погружать новичков и следить за и тем, как они развивают историю
- можно переиспользовать шаги, мы так делаем
Но главная мысль - каждый бизнес-процесс, как флоу - независимая история и мы описываем его здесь и сейчас не думаем о других процессах.
Если у нас в сервисе много разных бизнес-процессов - это сигнал задуматься и разнести бизнес-логику, чтобы упростить систему.
Если в компонентах есть какие-то общие шаги - их можно переиспользовать.
Например:
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
где запускается процесс обновления абонента по бизнес-правилам и там несколько сущностей обновить нужно, одну добавить.
Что самое важное: код там подобен примеру, просто чуть больше шагов и есть развилки. Я не стал специально усложнять этот пример - показал основные кирпичики.
Если инженер начнет экспериментировать с этой историей - он уже на конкретном примере начнет ощущать, на сколько она хороша.
В дальшейшем я планирую запустить один эксперимент, который наглядно демонстрирует насколько эта идея круто работает от фронта до бэка. Если вам будет интересно следить за обновлениями по теме - я скину линк на телеграм канал.
Надеюсь я ответил на ваши вопросы, если что-то не понятно - пишите.
Добрый день, спасибо за обратную связь!
Да, в целом, вы верно поняли мысль.
Вместо размазывания БЛ мы помещаем ее в объекты бизнес логики Entity, Aggregate. В примере исходных кодов я использую подход функциональной мутации имутабильных объектов (DDD made functional), в результате которого на выходе мы получаем запрос на изменение в Aggregate.
Важно обратить внимание на Type Driven Development при дизайне ValueObject-ов — это основной кирпичик + R.O.P. Строим на этом фундаменте вместе с unit тестами.
SOLID Мартина не заставляет, но единая ответственность может быть воспринята неоднозначно, что приводит к чрезмерной единой ответственности — 1 класс : 1 метод.
Запрос на изменение можно отправить-преобразовать в команду — дальше можно развивать историю и там дальше на пути встретимся с ивентами.
Я сделаю статью с примером по кукбуку - из исходников много можно будет чего вынести.
Также сделаю более подробные остановки на каждом пункте кукбука. В JPoint - была проба пера и я после этой записи много улучшений привнес.
Благодарю за обратную связь!
спасибо, и вариант названия мне нравится)
да, так и планирую. Каждый раздел - целая вселенная.
Следующая будет уже скоро и содержать больше подробностей.
К сожалению, общеизветные правила не все применяют и не применяют по делу