Глава 11. Еволюція Проектних Рішень
Глава 11. Еволюція Проектних Рішень
Вітаю! Якщо ви читаєте ці рядки, ви пройшли довгий шлях від визначення Єдиної Мови до реалізації складних Саг і Агрегатів.
Але найважливіший урок DDD ми приберегли на кінець: Всі архітектурні рішення — тимчасові.
Те, що було ідеальним рішенням для стартапу з 3 людей (Моноліт на Laravel/Django), стане смертним вироком для компанії з 500 інженерів. І навпаки: мікросервісна архітектура Netflix вб'є ваш стартап ще до першого релізу.
У цій главі ми поговоримо про Час. Як наша система повинна змінюватися разом з бізнесом.
Від CRUD до DDD
Закон Конвея
Refactoring to Deeper Insight
1. Життєвий Цикл Піддомену
У Главі 2 ми розділили світ на Core, Supporting та Generic domains. Але це не статичний поділ.
Generic -> Core
Уявіть, що ви будуєте інтернет-магазин у 2010 році.
- Доставка: Це просто поле "Shipping Address". Ви робите це як Generic Subdomain (CRUD).
- 2015 рік: Ви вирішуєте конкурувати швидкістю доставки. Ви запускаєте власних кур'єрів, дрони, склади.
- Result: Доставка перетворилася на Core Domain.
- Дія: Вам потрібно переписати модуль доставки. Виділити його в окремий контекст, застосувати складну логіку маршрутизації (Domain Model), можливо, виділити в мікросервіс.
Core -> Generic
- Пошук: Ви написали свій геніальний пошуковий двигун. Це ваш Core.
- 2020 рік: Elasticsearch/Algolia роблять це краще і дешевше.
- Дія: Ви викидаєте свій код і інтегруєте зовнішнє рішення. Ваш Core став Generic.
"Commoditization" trap
Найбільша помилка — триматися за свій кастомний код, коли світ вже придумав стандартне рішення. Якщо ви пишете свій Authentication Server у 2024 році замість використання Auth0/Keycloak — ви спалюєте гроші компанії. Аутентифікація — це Commodity.
2. Від Моноліту до Мікросервісів (і назад)
Тут ми не будемо холіварити. Ми подивимося на це через призму DDD.
Модульний Моноліт — Золота Середина
Більшість проектів повинні починатися і закінчуватися тут.
- Єдина база даних (але розділені схеми!).
- Один деплоймент юніт.
- Чіткі межі модулів (Context Boundaries).
- Публічний API модулів (Interfaces).
Коли виділяти Мікросервіс?
- Незалежне Масштабування: Модуль обробки відео вимагає GPU і 100 інстансів, а адмінка — 1 інстанс і 100мб RAM.
- Незалежний Релізний Цикл: Команда "Payment" боїться деплоїти свій код разом з нестабільним кодом "Recommendations".
- Технологічна Необхідність: Модуль ML краще писати на Python, а бекенд на C#/Java.
- Compliance: Дані кредитних карток (PCI DSS) повинні жити в ізольованому контурі.
Еволюція Context Mapping
Стосунки між командами теж змінюються.
- Partnership: На старті 2 команди сидять поруч. Вони домовляються про зміни в API за кавою.
- Customer-Supplier: Коли одна команда (Inventory) стає критичною для 5 інших, вона не може задовольняти хотілки кожного. Вона стає Upstream, інші — Downstream.
- OHS (Open Host Service): Inventory публікує стабільний API і каже: "Використовуйте це або йдіть лісом".
- ACL: Команда Sales не хоче залежати від змін Inventory, тому будує свій ACL.
3. Закон Конвея (Conway's Law)
"Організації, які проектують системи, змушені створювати проекти, які є копіями комунікаційних структур цих організацій." — Мелвін Конвей.
Якщо у вас є Команда Backend, Команда Frontend і Команда DBA — ви отримаєте тришарову архітектуру, де зміни в бізнес-логіці вимагатимуть синхронізації трьох менеджерів.
Inverse Conway Maneuver (Зворотній Маневр Конвея)
Якщо ви хочете мікросервісну (або модульну) архітектуру, сфокусовану на Бізнес-Доменах:
- Створіть Крос-функціональні команди (Cross-functional Stream-aligned teams).
- Дайте кожній команді володіння одним Обмеженим Контекстом (Catalog Team, Checkout Team).
- Архітектура підтягнеться під структуру команд.
4. Case Study: Refactoring The Big Ball of Mud
У вас є моноліт, де User клас має 5000 рядків, і все залежить від усього. З чого почати?
Крок 1: Зупинити кровотечу
Встановіть правило: Новий код пишеться в нових модулях.
Не додавайте 5001-й рядок у User.php. Створіть UserPreferenceService збоку.
Крок 2: Визначити межі (Logical Boundaries)
Почніть групувати код у папки за бізнес-змістом (Sales, Catalog), навіть якщо вони фізично знаходяться в одному проекті.
Використовуйте інструменти типу Deptrac (PHP) або ArchUnit (Java), щоб заборонити незаконні імпорти між цими папками.
Крок 3: Розв'язати вузли (Decoupling)
Найважча частина.
- Проблема:
Catalogнапряму лізе в таблицюusersчерез JOIN. - Рішення:
- Дублювання даних:
Catalogзберігає копію ім'я користувача (якщо це допустимо). - Події:
UserUpdated->Catalogоновлює свій кеш. - API:
CatalogзапитуєUserModule->getName($id).
- Дублювання даних:
5. Refactoring toward Deeper Insight
Ерік Еванс каже: "Справжнє DDD починається тоді, коли ви робите прорив у розумінні моделі".
Приклад: Booking vs Allocation
Спершу у нас була сутність Booking (Бронювання).
Ми думали: "Бронювання — це запис, що кімната зайнята".
Проблема: Бізнес каже "Ми хочемо овербукінг (продавати більше місць, ніж є)", "Ми хочемо переселяти людей", "Ми хочемо бронювати тип кімнати, а не конкретний номер".
Прорив (Insight): Ми зрозуміли, що є ДВІ різні концепції:
- Reservation: Обіцянка надати послугу (Юридичний контракт). Це про гроші і дати.
- Allocation: Прив'язка конкретного ресурсу (Room 101) до клієнта. Це про логістику.
Рефакторинг:
Ми розділяємо Booking на Reservation і RoomAllocation.
Це фундаментально змінює код, базу даних і API. Але це робить систему набагато гнучкішою. Тепер ми можемо мати Reservation без Allocation (овербукінг).
- Lesson: Не бійтеся змінювати імена і структуру класів, якщо змінилося ваше розуміння бізнесу. Код — це не відлитий у граніті пам'ятник, це глина.
6. Техніки Knowledge Crunching
Як досягти таких інсайтів? Не сидячи мовчки за клавіатурою.
Event Storming
Зберіть в одній кімнаті (або Miro) розробників, тестувальників, менеджерів і справжніх юзерів.
- Помаранчеві стікери: Domain Events (Що сталося? "Замовлення Створено").
- Блакитні стікери: Commands (Хто ініціював? "Клієнт натиснув кнопку").
- Жовті стікери: Aggregates (Де живе логіка?).
За 2 години Event Storming ви знайдете більше багів і невідповідностей у вимогах, ніж за 2 місяці Jira-переписки.
Whirlpool Process (Вир)
Процес моделювання не лінійний. Це постійний цикл:
- Спробували модель.
- Написали код.
- Знайшли кейс, який модель не покриває ("А що якщо клієнт — це компанія?").
- Змінили модель.
- Refactor.
Модель, яка не змінюється протягом року — це мертва модель.
7. Висновок
Domain-Driven Design — це не про патерни. Не про Репозиторії, Агрегати чи Value Objects.
Це про Спілкування. Про те, щоб розробники і бізнес-експерти говорили однією мовою і будували модель, яка вирішує реальні проблеми.
Успіхів у ваших проектах! Нехай ваш код буде чистим, а домен — багатим.
Що читати далі?
- Eric Evans - Domain-Driven Design (The Blue Book) — філософія.
- Vaughn Vernon - Implementing DDD (The Red Book) — практика.
- Nick Tune / Kacper Gunia - Architecture Patterns with Python/C# (Modern approach).
- Vladik Khononov - Learning Domain-Driven Design (Найкраще для старту).
Глава 10. Проектні Евристики
"It depends" (Це залежить) — найпопулярніша відповідь будь-якого консультанта чи архітектора. Але від чого саме це залежить?
Глава 12. EventStorming
Ми розглянули безліч інструментів DDD для аналізу та моделювання. Але як застосувати їх, коли ви працюєте не один, а з групою людей, які мають різний рівень розуміння бізнесу та технологій? Тут на сцену виходить EventStorming.