Изоляция компонентов нужна для управления сложностью, особенно для больших команд и программ. Но её раннее применение часто противоречит целей по гибкости, особенно в начале, когда ещё не известны или не сложились бизнес процессы, когда программу ещё пишет небольшая команда.
Изменение границ компонент это сложно. В ООП это сложнее чем в ФП из-за более крупной гранулярности, класс сложнее чем отдельная функция. Изменение границ микросервисов сложнее чем в монолите.
Проблема ранней изоляции: —
Необходимость частого рефакторинга на раннем этапе
Тонкая прослойка как компромисс: — Например, обёртки над библиотечными классами добавляют функционал с минимальным вмешательством и без необходимости писать всё с нуля.
Гибридный подход: — Функциональный домен, императивная оболочка.
Домен — функциональное ядро: Бизнес-логика в чистых функциях, независимых от инфраструктуры.
Оболочка — императивная. Необходимость интеграции с ООП библиотеками (io, фреймворки) заставляет писать эту часть в ООП. Но можно использовать тонкие прослойки для интеграции для минимизации boilerplate.
Риски: — Скрытая привязка к библиотеке: Домен косвенно зависит от её типов. — Сложность замены: Если библиотека всё же потребует замены, придётся модифицировать методы расширения.
Итог: Ранняя изоляция через толстые адаптеры часто избыточна.
Методика
Как же тогда должна выглядеть методика обследования и проектирования на основе DDD, но оставляя гибкими границы доменов по bounded context до нужного момента?
Методика пригодна как для проектирования архитектуры доменных компонент, так и более мелкого уровня детализации (структуры данных и функции).
Определения (мои)
-
Состояние — наблюдаемое явление, фиксированный набор значений параметров системы или её части в определённый момент времени, определяющий её поведение и доступные действия.
-
Типы → Категории возможных состояний системы и их допустимых комбинаций.
-
События → Факты о произошедших изменениях (прошлые состояния).
-
Процессы → Механизмы перехода между состояниями (будущие изменения состояний).
Последовательность проектирования компонент (домена DDD либо более мелких сущностей):
1. Анализ домена:
— Event Storming → выявление ключевых событий и процессов.
— Определение Bounded Context → границы модулей. Домен должен быть такой чтобы помещался а голове одного доменного эксперта. Если говорить про модули кода, то принцип единственной ответственности не позволит вырасти когнитивной сложности компонента, так как не смешивает разные предметные области в одном коде.
2. Моделирование типами:
— Каждое доменное понятие → алгебраический тип данных, определяет набор возможных состояний и их комбинаций.
— Бизнес-правила → валидаторы как функции.
3. Чистые функции для логики:
-
Мелкая гранулярность в ФП позволит легко переносить функционал через границу компонент
-
Сервисы → композиция pure-функций
-
Обработка ошибок → Either/Result типы.
4. Изоляция эффектов и состояний:
IO-операции → отдельный слой (Http, БД).
Использование СУБД выноса состояний наружу или монад для отсутствия скрытых побочных эффектов / состояний. Если это сложно или не эффективно, то для реализации состояний можно использовать состояние объекта в ООП. Также ООП имеет преимущество если ООП библиотеки уже реализуют почти всю логику вашей предметной области и ваши добавления незначительны.
5. Оптимизация скорости (при необходимости)
Когда же тонкая и гибкая прослойка должна стать жёсткой и надёжно изолирующей?
Выбор между тонкой и толстой прослойкой между компонентами зависит от контекста:
1. Тонкая прослойка (быстро/минимализм) или даже вызовы библиотек напрямую, жертвуя гибкостью и принципом единственной ответственности.
— Подходит, если термины библиотеки на 80% совпадают с вашим доменным языком.
— Подходит когда граница компонент ещё не сложилась, она может и должна легко меняться.
— Риск: Привязка к библиотеке, но экономия времени на разработку.
2. Толстая прослойка (надёжность/изоляция):
— Нужны доменные модели, полностью абстрагированные от библиотек.
— Границы компонент не меняются.
— Нужно надёжно изолировать компоненты, чтобы изменения в одних не требовали цепочку изменений других. Особенно, если другими компонентами уже владеет другая группа разработки.
Компромисс:
Даже минимальные адаптеры (интерфейсы + базовые преобразования) лучше, чем прямое использование библиотеки в ядре. Это сохраняет путь к будущим изменениям без переписывания системы.
Правило выбора:
Чем критичнее компонент для бизнеса и чем более стабильны границы между компонентами, тем «толще» должна быть прослойка между компонентами.
ссылка на оригинал статьи https://habr.com/ru/articles/882826/
Добавить комментарий