Глава 13. DDD на Практиці
Глава 13. Предметно-Орієнтоване Проектування на Практиці
Теорія DDD звучить чудово. В ідеальному світі ми починаємо нові проекти (Greenfield), формуємо ідеальну команду, де кожен експерт володіє термінологією DDD, і створюємо бездоганну модель.
Але реальність, з якою ми стикаємося щодня, виглядає інакше:
- Ми працюємо з Brownfield проектами (існуючий код, написаний 5-10 років тому).
- У нас є Big Ball of Mud (Великий Комок Бруду).
- Бізнес вимагає нових фіч "на вчора", а не рефакторингу.
- Команда не знає (і часто не хоче знати) про Агрегати та ВО.
У цій главі ми поговоримо про те, як застосовувати DDD в реальних, недосконалих умов. Як бути прагматичним, а не догматичним.
1. Стратегічний Аналіз Legacy
Перш ніж кидатися переписувати код, потрібно зрозуміти ландшафт. Згадайте стратегічні патерни з Частини I. Вони тут навіть важливіші, ніж у новому проекті.
Оцінка Ландшафту
Запустіть процес Knowledge Crunching (наприклад, EventStorming з Глави 12), щоб відновити втрачені знання про систему. Вам потрібно відповісти на питання:
- Де гроші? Який піддомен є Core Domain?
- Де біль? Який модуль має найбільше багів і найвищу складність змін?
- Яка структура? Які є високорівневі компоненти і як вони пов'язані?
Класифікація Піддоменів
Не намагайтеся рятувати все.
- Generic / Supporting Subdomains: Якщо тут "бардак", але воно працює і рідко змінюється — не чіпайте. Витрачати ресурси на рефакторинг адмінки, яка працює — це марнотратство.
- Core Domain: Це ваш пріоритет. Тут "бардак" коштує бізнесу грошей (повільні зміни, баги в розрахунках). Саме тут варто впроваджувати DDD.
2. Стратегії Модернізації
"Переписати з нуля" (Big Rewrite) — це майже гарантований провал. Замість революції, обирайте еволюцію.
Патерн Strangler Fig (Фікус-Душитель)
Цей патерн названо на честь рослини, яка росте на дереві-хазяїні, поступово обвиває його і з часом замінює, залишаючи дерево вмирати всередині.
Алгоритм:
- Створіть новий модуль/сервіс поруч зі старим.
- Реалізуйте нову функціональність в новому модулі.
- Налаштуйте фасад (API Gateway або Load Balancer) для перенаправлення трафіку.
- Поступово переносьте існуючу функціональність зі старого в новий.
- Коли старий модуль спорожніє — видаліть його.
Bubble Context (Контекст-Бульбашка)
Якщо ви не можете виділити окремий сервіс, створіть "чисту зону" всередині моноліту. Це ваш новий Bounded Context.
- Legacy:
Dictionary<string, object> UserData - ACL: Перетворює це на...
- Bubble Context:
class Customer { Id id; Email email; }
Tactical Forking (Тактичне Розгалуження)
Іноді розплутувати залежності ("клубок спагетті") занадто дорого. Якщо вам треба розділити моноліт на два (наприклад, "Продажі" і "Логістика"), і код сильно зв'язаний:
- Зробіть копію всього проекту (Copy-Paste).
- В одній копії ("Продажі") видаліть все, що стосується Логістики.
- В іншій копії ("Логістика") видаліть все, що стосується Продажів. Це брудно, але це працює, коли розчепити залежності на рівні класів неможливо.
3. Тактичний Рефакторинг
Як перейти від Transaction Script (процедурний код) до Domain Model (ООП)? Робимо це крок за кроком.
1. Виділення Сервісного Шару
Якщо у вас "Товсті Контролери" (вся логіка в API endpoint), винесіть логіку в Сервіси.
OrderController -> OrderService.
Це підготує ґрунт.
2. Заміна Примітивів на Value Objects
Це найдешевший спосіб підвищити якість коду.
Знайдіть string email, decimal amount, string currency.
Замініть на Email, Money.
Це додасть валідацію і логіку в самі дані.
3. Anemic -> Rich Model
Зараз ваш Order — це просто набір полів (DTO). Логіка лежить в OrderService.
Пересувайте методи в всередину класу Order.
service.CalculateTotal(order) -> order.CalculateTotal().
Принцип: Tell, Don't Ask. Не запитуйте дані у об'єкта, щоб щось з ними зробити. Скажіть об'єкту зробити це.
4. Виділення Агрегатів
Коли логіка вже всередині об'єктів, визначте межі транзакцій.
Що має зберігатися разом? Order + OrderLine.
Захистіть інваріанти. Зробіть сетери приватними (private set). Змінюйте стан тільки через публічні методи з бізнес-назвами (AddItem, Confirm).
4. Як "продати" DDD бізнесу?
Якщо ви прийдете до менеджера і скажете: "Нам треба переписати систему на Агрегати, бо це правильно", вас виженуть. Бізнес не купує патерни. Бізнес купує вирішення проблем.
Аргументи для бізнесу
- Зменшення кількості помилок (Bugs): "Минулого місяця ми 3 рази фіксили розрахунок знижки. Якщо ми виділимо це в окремий модуль з чіткими правилами, це не повториться".
- Time-to-Market: "Зараз додавання нової кнопки займає тиждень, бо ми боїмося зламати старе. З ізольованими контекстами ми зможемо деплоїти це за день".
- Onboarding: "Новим розробникам потрібно 3 місяці, щоб зрозуміти цей код. Якщо ми введемо "Єдину Мову" (Ubiquitous Language), вони зрозуміють бізнес за 2 тижні".
Партизанське DDD (Covert DDD)
Іноді краще взагалі не використовувати терміни DDD. Не кажіть "Bounded Context". Кажіть "Модуль" або "Продукт". Не кажіть "Ubiquitous Language". Кажіть "Словник термінів". Просто робіть це. Використовуйте правильні імена в коді. Будуйте чисті моделі. Ваша робота — писати якісний код, і вам не завжди потрібен дозвіл на використання правильних інструментів.
5. Висновок
DDD — це не срібна куля. Це набір інструментів для боротьби зі складністю.
- Використовуйте Стратегічне DDD (Context Maps, Subdomains), щоб зрозуміти що ви будуєте. Це корисно завжди, в будь-якому проекті.
- Використовуйте Тактичне DDD (Aggregates, Value Objects) тільки там, де це виправдано складністю (Core Domain).
Ніколи не застосовуйте DDD тільки заради того, щоб поставити галочку в резюме. Головна мета — створити програмне забезпечення, яке приносить цінність і яке можна підтримувати роками.
Вправи
- Ви працюєте над Legacy-системою електронної комерції. Модуль "Знижки" реалізований як набір SQL-процедур на 2000 рядків. Він постійно ламається.
Яку стратегію модернізації ви оберете?
- Bubble Context?
- Strangler Fig?
- Rewrite from scratch?
- Ваш менеджер каже: "Ми не маємо часу на EventStorming, просто почніть кодити". Яка ваша відповідь? (Виберіть найбільш прагматичну).
- Чому тактичне розгалуження (Tactical Forking) називають "брудним" методом і коли воно виправдане?
Кінець. Дякуємо, що були з нами у цій подорожі світом Domain-Driven Design!
Глава 12. EventStorming
Ми розглянули безліч інструментів DDD для аналізу та моделювання. Але як застосувати їх, коли ви працюєте не один, а з групою людей, які мають різний рівень розуміння бізнесу та технологій? Тут на сцену виходить EventStorming.
01. Магія Стрімінгу: Що відбувається, коли ви натискаєте "Play"