Канал о разработке поддерживаемых бакэндов - про классическую школу TDD, прагматичное функциональное программирование и архитектуру и немного DDD.
Группа: https://t.me/+hrqD87p0Oa
Канал в Max: https://max.ru/id544512614458_biz
https://azhidkov.pro
Post #617
600
Привет!
У нас в группе за последнюю пару недель было сразу два холивара на тему разделения на слой приложения и домена в DDD. Я смотрел на них и про себя думал, что это очень попахивает искусственной/побочной сложностью.
В ЭП тоже есть очень похожее разделение и даже с теми же названиями, потому что выросло оно у меня из ДДД. Но у меня оно сейчас если не проще, то чётче, на мой взгляд.
В слое домена у меня живёт состояние - классы сущностей/агрегатов и репозитории*.
В слое приложения живут операции системы - в первую очередь порты/точки входа - хттп эндпоинты, обработчики крон-задач, слушатели всевозможных событий и сообщений.
Если операция системы простая - меняет только один ресурс - то она реализуется прямо в порте. В идеале - единственным вызовом репозитория, но см. правила ниже.
Если же операции надо потрогать два или более репозитория, то она оформляется в отдельный класс операции с единственным публичным методом, который помещается в слой приложения и может звать уже сколько угодно репозиториев. Но тоже см. правила ниже.
И для выбора места размещения кода у меня есть два подхода на выбор разработчика.
1. снизу вверх. Код помещается на максимально низкий уровень, где доступно требуемое состояние. Трансоформация/вычисление на базе одной сущности помещается рядом с ней. Код, меняющий только один репоз помещается рядом с этим репозом. Код трансформирующий/вычисляющий на базе нескольких агрегатов/сущностей помещается в слой приложения рядом с операцией, которая его использует или в общий код пакета, содержащего все операции, которые его используют. Код работающий с несколькими ресурсами помещается аналогично максимально близко к операции(ям), его использующим
2. сверху вниз. Код помещается на самый высокий уровень, доступный всем его клиентам. Этот подход, на самом деле имеет смысл только для трансформаций сущностей и тут по большому счёту только два варианта. Трансформация одной сущности, которая используется только в одной операции помещается рядом с ней. Если он нужен нескольким операциям - помещается рядом с сущностью.
Я обычно придерживаюсь подхода снизу вверх, вынося трансформации выше если:
1. она явно относится к одной конкретной операции
2. вокруг сущности накопилось более 10 методов, многие из которых используются только в одной операции
Кроме того у меня есть пачка защитных-правил, которые предотвращают превращение реализации в кровавое месиво бизнес-логики и ввода-вывода и разных уровней абстракции:
1. каждая технология должна быть инкапсулирована либо в порте, либо в ресурсе. При том в случае ресурса инкапсуляция означает, что технология не фонит в API ресурса. В АПИ репозитория не должны упоминаться таблицы, колонки, внешние ключи и т.п. В АПИ хттп клиента внешней системы не должны упоминаться хттп методы, заголовки, куки, мультипарты и т.д.
2. Ни порты, ни операции, ни репозитории** не могут вызывать друг друга
3. метод порта не может вызывать более одной операции или метода ресурса
4. метод порта может содержать только одну конструкцию ветвления (if/when/switch) для выбора представления ответа
5. метод, который прямо или косвенно содержит ввод-вывод (методы портов, операций и репозиториев) может иметь когнитивную сложность не более 4
6. классы поведения (операции, ресурсы) должны иметь не более 7, край 10 зависимостей. А в идеале - до 5
В общем, мне кажется, хороший подход/гайдлайн/методика разработки должен минимизировать поле для холиваров и разночтений. А для этого он должен быть определён в максимально чётко сформулированных правилах, проверку которых, в идале, можно автоматизировать.
Подробнее об этом у меня написано в заготовке статьи Эргономичная архитектура
#ergo_approach@ergonomic_code #ddd@ergonomic_code
У нас в группе за последнюю пару недель было сразу два холивара на тему разделения на слой приложения и домена в DDD. Я смотрел на них и про себя думал, что это очень попахивает искусственной/побочной сложностью.
В ЭП тоже есть очень похожее разделение и даже с теми же названиями, потому что выросло оно у меня из ДДД. Но у меня оно сейчас если не проще, то чётче, на мой взгляд.
В слое домена у меня живёт состояние - классы сущностей/агрегатов и репозитории*.
В слое приложения живут операции системы - в первую очередь порты/точки входа - хттп эндпоинты, обработчики крон-задач, слушатели всевозможных событий и сообщений.
Если операция системы простая - меняет только один ресурс - то она реализуется прямо в порте. В идеале - единственным вызовом репозитория, но см. правила ниже.
Если же операции надо потрогать два или более репозитория, то она оформляется в отдельный класс операции с единственным публичным методом, который помещается в слой приложения и может звать уже сколько угодно репозиториев. Но тоже см. правила ниже.
И для выбора места размещения кода у меня есть два подхода на выбор разработчика.
1. снизу вверх. Код помещается на максимально низкий уровень, где доступно требуемое состояние. Трансоформация/вычисление на базе одной сущности помещается рядом с ней. Код, меняющий только один репоз помещается рядом с этим репозом. Код трансформирующий/вычисляющий на базе нескольких агрегатов/сущностей помещается в слой приложения рядом с операцией, которая его использует или в общий код пакета, содержащего все операции, которые его используют. Код работающий с несколькими ресурсами помещается аналогично максимально близко к операции(ям), его использующим
2. сверху вниз. Код помещается на самый высокий уровень, доступный всем его клиентам. Этот подход, на самом деле имеет смысл только для трансформаций сущностей и тут по большому счёту только два варианта. Трансформация одной сущности, которая используется только в одной операции помещается рядом с ней. Если он нужен нескольким операциям - помещается рядом с сущностью.
Я обычно придерживаюсь подхода снизу вверх, вынося трансформации выше если:
1. она явно относится к одной конкретной операции
2. вокруг сущности накопилось более 10 методов, многие из которых используются только в одной операции
Кроме того у меня есть пачка защитных-правил, которые предотвращают превращение реализации в кровавое месиво бизнес-логики и ввода-вывода и разных уровней абстракции:
1. каждая технология должна быть инкапсулирована либо в порте, либо в ресурсе. При том в случае ресурса инкапсуляция означает, что технология не фонит в API ресурса. В АПИ репозитория не должны упоминаться таблицы, колонки, внешние ключи и т.п. В АПИ хттп клиента внешней системы не должны упоминаться хттп методы, заголовки, куки, мультипарты и т.д.
2. Ни порты, ни операции, ни репозитории** не могут вызывать друг друга
3. метод порта не может вызывать более одной операции или метода ресурса
4. метод порта может содержать только одну конструкцию ветвления (if/when/switch) для выбора представления ответа
5. метод, который прямо или косвенно содержит ввод-вывод (методы портов, операций и репозиториев) может иметь когнитивную сложность не более 4
6. классы поведения (операции, ресурсы) должны иметь не более 7, край 10 зависимостей. А в идеале - до 5
В общем, мне кажется, хороший подход/гайдлайн/методика разработки должен минимизировать поле для холиваров и разночтений. А для этого он должен быть определён в максимально чётко сформулированных правилах, проверку которых, в идале, можно автоматизировать.
Подробнее об этом у меня написано в заготовке статьи Эргономичная архитектура
#ergo_approach@ergonomic_code #ddd@ergonomic_code
- ❤ 5
- 👍 3



