Bounded Context і межі домену
Bounded Context (BC) - це чітка межа, всередині якої діє єдиний Ubiquitous Language, узгоджені моделі та інваріанти. Усередині межі терміни однозначні («Ставка», «Клієнт», «Ліміт»), а назовні контекст спілкується контрактами (подіями/командами) і не тягне всередину чужі смислові «хвости». Грамотно обрані межі знижують зв'язність, спрощують масштабування і прискорюють еволюцію продукту.
1) Навіщо потрібні межі
Зниження когнітивного навантаження. Команда працює з однією моделлю і однією мовою, а не з «всім бізнесом відразу».
Ізоляція інваріантів. Критичні правила (баланс ≥ 0, унікальність логіна) живуть в одному місці і захищені агрегатами.
Управління змінами. Еволюція схеми/правил всередині BC не ламає сусіда - є явні контракти.
Продуктивність і надійність. Всередині BC можна вибрати відповідну модель узгодженості і сховище; зовні - асинхронні проекції.
2) Як виявляти Bounded Context
Швидкий метод (workshop 2-4 години):1. Event Storming: випишіть доменні події «що сталося», потім команди «що просимо зробити», потім агрегати «хто гарантує правило».
2. Кластери мови: де слова і правила стабільно збігаються - потенційний BC. Де слово «Клієнт» значить різне (платник vs гравець) - там явно різні контексти.
3. Інваріанти та ownership: що не можна порушити і хто відповідає? Інваріант → всередину того BC, який може його гарантувати.
4. Потік цінності: згрупуйте кроки, які часто змінюються разом - це кандидати на один BC.
5. Орг-структура: якщо одну частину робить окрема команда з окремими KPI - ймовірно, це окремий BC (але не навпаки: оргструктура не повинна сліпо диктувати модель).
Сигнали кордону:- Суперечка про терміни («ставка», «квиток», «раунд» - різні смисли).
- Найгарячіший інваріант «протікає» через сервіси.
- Різні SLO і темп змін.
- «Dual-write» між модулями заради атомарності.
3) Типові контексти (приклад предметної області)
Identity/KYC - реєстрація, рівні верифікації, статуси обмежень.
Wallet/Ledger - баланси, проводки, резерви, валюти.
Betting/Orders - прийом, котирування, розрахунок.
Game/Round - життєвий цикл раунду, результати.
Bonus/Promo - нарахування, вейджер, конверсія.
Payments - депозити/висновки, статуси платіжних шлюзів.
Compliance/Reporting - звіти, аудит, регуляторні вітрини.
Catalog/Provider Integration - ігри, версії, статуси провайдерів.
Analytics/Read Models - проекції та матеріалізовані уявлення.
4) Context Map: як BC взаємодіють
Карта контекстів фіксує тип відносин:- Customer–Supplier. Один BC (Supplier) поставляє події/дані, інший (Customer) підлаштовує свої моделі.
- Conformist. Customer приймає мову і модель Supplier як є (наприклад, нормативний реєстр).
- Partnership. Два BC синхронно еволюрують мову і контракти (часто - одна команда/roadmap).
- Shared Kernel. Загальний мінімальний під'язик/бібліотека, версіонується спільно; використовувати обережно.
- Anti-Corruption Layer (ACL). Захисний шар, що переводить чужі моделі в свою мову.
- Open Host Service / Published Language. Публічні протоколи/схеми, що версіонуються та документовані.
Практика: за замовчуванням використовуйте ACL і асинхронні події; Conformist - тільки якщо провайдер диктує стандарт, Shared Kernel - мінімально і усвідомлено.
5) Межа = мова + модель + інваріанти + сховище
Всередині BC визначте:- Ubiquitous Language. Словник термінів з прикладами.
- Агрегати та інваріанти. Хто «тримає» правила і які операції дозволені.
- Модель узгодженості. Strong/CP для грошей, EC/causal для вітрин.
- Сховище та індекси. Вибираються під інваріанти і SLO.
- Контракти виходу. Події/команди, версії схем, SLO доставки.
Зовні: ніяких прямих SQL/табличних залежностей. Спілкування - через контракт.
6) Межі та узгодженість (PACELC)
Всередині BC: вибирайте модель під інваріанти (Wallet - Strong, Betting - Strong на прийомі).
Між BC: найчастіше eventual через події і проекції. Якщо потрібна синхронна перевірка - явна команда з дедлайном і відмовою при недоступності (не «прихований» REST-поклик).
7) Антикорупційний шар (ACL)
Завдання ACL: не пустити чужу мову і «брудні» дані всередину BC.
Маппінг схем: зовнішнє'PaymentStatus = SETTLED'→ внутрішнє'LedgerEntry (type = Credit, reason = PsPSettle)'.
Валідація і збагачення: перевірка інваріантів, нормалізація таймзон, валют.
Версіонування: підтримка'schema _ version'зовнішнього контракту, зворотна сумісність.
Ідемпотентність: по `external_id`/`operation_id`.
Спостережуваність: трейс-теги'source','schema _ version','mapping _ id', DLQ для «отруйних» повідомлень.
8) Межі та дані: володіння, проекції, API
Ownership: хто володіє «істиною»? Тільки власник змінює запис. Іншим BC - read-моделі і посилання.
Проекції: денормалізовані таблиці під читання; оновлюються з подій.
API: команди (мутують у власника) і запити (читають проекції). Ніяких «наскрізних» апдейтів чужих даних.
9) Еволюція і версії
Події і API - з'schema _ version'і політикою сумісності (additive + fallback).
Blue/Green по BC: новий контракт «v2» публікується паралельно «v1», трафік переводиться поступово.
Міграції: для серйозних змін - нова проекція/сервіс, «двофазний світч» читань.
10) Тестування кордонів
Contract tests: перевірка, що BC дотримується опублікований контракт (producer tests) і коректно розуміє чужий (consumer tests).
Property-based: інваріанти агрегатів всередині BC (баланс, ліміти, унікальності).
Chaos на інтеграціях: затримки, out-of-order, дублікати, schema-evolution; наявність DLQ і безпечного редрайву.
NFR-тести: р95/пікове навантаження на кордоні (сервер подій/ACL).
11) Спостережуваність і SLO по кордонах
Метрики: throughput подій/команд,'projection _ lag _ ms','dlq _ rate', помилки маппінгу, p95 API.
Трейсинг: обов'язкові теги'bc','tenant _ id','event _ id','operation _ id','schema _ version'.
Алерти: перевищення лага проекцій, зростання відмов команд, «флап» схеми (багато'schema _ mismatch').
12) Мульти-тенант і регіони
'tenant _ id'- в ключах всіх подій і проекцій на кордоні.
Fairness: ліміти на публікації/редрайв per tenant, щоб «галасливий» не зривав SLO сусідів.
Residency: дані BC живуть в «домашньому» регіоні; крос-регіонально - агрегати/звіти.
13) Анти-патерни (до чого призводить розмита межа)
Гігантський «core-service». Все в одному місці → боротьба за транзакції, довгі релізи, низька автономність.
Табличні інтеграції. Прямі SELECT в чужі таблиці → крихкість і coupling за схемою.
Dual-write. Одночасно писати в два BC «для зручності» → розбіжності і саботаж інваріантів.
Conformist за замовчуванням. «Прийняли чужу модель як є» → витік чужих смислів, неможливість еволюції.
Приховані синхронні виклики. REST-поклик «десь всередині» без явного контракту і дедлайну → несподівана залежність за доступністю.
14) Приклад контурів (словесна схема)
[Wallet/Ledger] <--CP, Leader, Transactions-->
publishes: WalletReserved/Committed v
[Betting] <--CP on bid taking-->
events: BetPlaced/Settled v
[Read Models/Analytics] <--EC projection-->
[Payments] --ACL--> [Wallet/Ledger]
[Provider Integration] --ACL--> [Game/Round]
[Compliance] <-events - [KYC/Identity], -> reports [Reporting]
15) Міні-гайд за вибором кордону
1. Сформулюйте інваріанти і визначте, хто їх може гарантувати.
2. Опишіть словник (10-20 термінів) і перевірте, що у команди одне розуміння.
3. Намалюйте Context Map і типи відносин.
4. Вирішіть модель узгодженості всередині і на стиках.
5. Спроектуйте контракти (події/команди) і ACL.
6. Заплануйте спостережуваність (метрики/трейсинг/алерти) і DLQ/редрайв.
7. Проведіть contract-tests і «шторму» (chaos) для інтеграцій.
8. Зафіксуйте governance: хто володіє мовою/схемою, як вносяться зміни.
16) Чек-лист перед продом
- У кожного BC є словник, агрегати та інваріанти.
- Визначені відносини на Context Map і документовані контракти.
- Інтеграції через події/команди і ACL, немає прямих SQL-залежностей.
- Ідемпотентність команд/подій; є outbox/inbox і DLQ.
- Модель узгодженості (всередині/між BC) зафіксована і протестована.
- Версіонування схем і стратегія сумісності (v1/v2).
- Метрики лага/помилок/продуктивності і алерти налаштовані.
- Політики мульти-тенантності і data-residency дотримані.
- Операційні плейбуки: schema-mismatch, redrive, rebuild проекцій.
17) Швидкі рецепти
Гроші та ліміти: окремий BC з CP і транзакціями, API тільки командами, події як результат істини для читань.
Фіди/каталоги: BC з EC, проекції і кеш, явний'freshness'.
Інтеграції з зовнішніми провайдерами: завжди через ACL, події/команди, версіонування схем.
Зростання команди: один BC - одна команда, у команди є «власник мови» і «зберігач інваріантів».
Рефакторинг моноліту: спочатку контракти і ACL, потім фізичний поділ.
Висновок
Bounded Context - це не тільки діаграма, а робочий договір про мову, правила і спосіб еволюції. Чіткі межі зменшують зв'язність, прискорюють зміни і роблять систему передбачуваною в експлуатації. Розділяйте за змістом і інваріантам, захищайте межі ACL і контрактами, вимірюйте всі метриками - і ваша архітектура залишиться гнучкою і надійною навіть при стрімкому зростанні домену і команди.