open atlas
↑ К треку
Архитектурные паттерны ARCH · 06 · 01

Bounded contexts

Bounded context — явная граница, внутри которой живёт одна согласованная доменная модель. Одно и то же слово — Order, Customer, Product — означает разную модель в каждом контексте. Bounded context — основная единица декомпозиции в DDD.

ARCH Senior ◷ 24 min
Уровень
ОсновыJuniorMiddleSenior

На B2B-платформе заказов была одна сущность «Order», разделённая на весь бэкенд. У неё было 47 полей. Команда продаж нуждалась в Order для фиксации согласованных позиций, уровней скидок и закреплённого менеджера. Команда биллинга нуждалась в Order для отслеживания сумм счетов, условий оплаты и налоговой юрисдикции. Команда доставки нуждалась в Order для адреса доставки, размеров упаковки и предпочтений перевозчика. Каждая команда добавляла поля в один и тот же класс. Класс рос. Поля для Sales ничего не значили для Billing. Поля для Billing запутывали сервис Shipping. Правила валидации конфликтовали: Sales говорил, что Order валиден с момента подписания соглашения; Billing — только когда подтверждён способ оплаты; Shipping — только когда склад взял товар в работу. Три команды, три определения «валидного Order», все борются за один класс. Старший архитектор, пришедший в команду, задал один вопрос: «Вы правда думаете, что Sales, Billing и Shipping говорят об одном и том же, когда произносят “Order”?» Нет. Сущность имела три разных идентичности, слитых в одну. Починка состояла не в добавлении флагов валидации. Она состояла в проведении трёх явных границ и выдаче каждой команде собственной модели.

Центральная проблема: одно слово — много моделей

Domain-driven design начинается с наблюдения, на котором спотыкается почти каждая большая система: естественный язык неоднозначен. Слово «Customer» в CRM означает контакт с историей продаж, владельца отношений и лид-скор. В биллинговой системе «Customer» — это аккаунт со способом оплаты, кредитным лимитом и непогашенными счетами. В системе доставки «Customer» — это адрес доставки и контактный телефон.

Если в вашем коде один класс Customer, разделяемый всеми тремя системами, произойдёт одно из двух. Либо класс вырастет и вберёт в себя все три смысла (God-объект на 60 полей, который ни одна команда полностью не понимает), либо класс несёт смысл той команды, которая написала его первой, и каждая другая команда постоянно адаптируется вокруг полей, которые к ней не относятся.

Прозрение Эрика Эванса в Domain-Driven Design (2003): попытка объединить это в одну модель — не цель, а ошибка. У каждого поддомена свой язык, и этот язык порождает свою модель. Цель — не единая унифицированная модель, а набор моделей, каждая из которых внутренне согласована в своих границах.

Bounded context — это граница. Явное объявление: «Внутри этого контекста слово “Customer” означает именно вот это, слово “Order” означает именно вот это, и эта модель внутренне согласована». За пределами контекста то же слово может означать что-то другое — и это нормально, потому что граница сделана явной.

Субдомены vs bounded contexts: пространство проблем vs пространство решений

Эти два термина часто используют как синонимы — напрасно. Разница — между тем, чем занимается бизнес, и тем, как ваш код это организует.

Субдомен — часть пространства бизнес-проблем: он обнаруживается в разговорах с доменными экспертами и существует вне зависимости от того, как вы строите ПО. У вашего бизнеса есть субдомен Sales, субдомен Billing, субдомен Shipping. Субдомены бывают ключевыми (core — дифференцируют бизнес), вспомогательными (supporting — необходимы, но не дифференцируют) и обобщёнными (generic — решённые проблемы, покупайте готовое).

Bounded context — решение в пространстве решений: оно проектируется вашей командой. Один субдомен может обслуживаться одним bounded context (типичный случай) или несколькими (когда большой ключевой домен требует отдельных команд). Один bounded context может охватывать несколько субдоменов (иногда делается для простых вспомогательных субдоменов).

Эвристика: сначала определите субдомены (в разговорах с бизнесом), затем решите, сколько bounded contexts создать для каждого — исходя из топологии команд, сложности модели и потребностей деплоя.

Почему это работает

Почему разница важна на практике? Потому что команды часто путают их и получают границу bounded context, не совпадающую ни с какой реальной бизнес-границей. Разделение контекстов, продиктованное технологией («у нас будет микросервис для аутентификации») без соответствующего разделения моделей, просто создаёт вызов по RPC там, где раньше был вызов функции — та же логическая модель, теперь распределённая. Граница bounded context значима только когда модели по обе стороны подлинно различаются.

Что на самом деле содержит bounded context

Bounded context — это не только граница сервиса или базы данных. Это языковая и модельная граница, содержащая:

  • Ubiquitous language — точный словарь, разделяемый разработчиками и доменными экспертами в этом контексте. «Order» в Sales-контексте имеет одно специфическое определение, которое использует каждый разработчик и каждый продакт-менеджер этой команды.
  • Доменная модель — классы, агрегаты, value objects и правила, реализующие этот словарь. Модель валидна и согласована внутри границы. Ничто снаружи не должно напрямую обращаться к её внутренностям.
  • Явный интерфейс интеграции — граница явна. Другие контексты интегрируются через события, API или слои трансляции — не импортируя внутренние классы.

Граница применяется в коде: в монолите — границы пакетов, проверяемые архитектурными тестами; в сервисной архитектуре — граница сервиса и есть граница контекста.

Основная единица декомпозиции

В стратегическом DDD bounded contexts — основная единица декомпозиции. Прежде чем спросить «сколько у нас должно быть микросервисов?», вы спрашиваете: «Сколько bounded contexts имеет этот домен, и какова модель в каждом?»

Каждый bounded context соответствует: одной команде с чётким владением, одному ubiquitous language, разделяемому разработчиками и доменными экспертами, одной кодовой базе (или модулю с принудительными границами), одной единице деплоя (в сервисной архитектуре).

Викторина

На B2B-платформе есть один общий класс `Order`, используемый Sales, Billing и Shipping. Класс вырос до 47 полей. Разработчик предлагает: «Разделим на три подкласса — SalesOrder, BillingOrder, ShippingOrder, все наследуют от базового Order». Почему это не решает проблему bounded context?

Викторина

Команда выявила три субдомена: управление заказами (ключевой), расчёт налогов (вспомогательный) и email-уведомления (обобщённый). Джуниор предлагает по одному bounded context на субдомен. Старший архитектор говорит: «Для Tax и Email нужно использовать готовое ПО — значит, они вообще не наши bounded contexts». В чём логика архитектора?

Викторина

Два инженера обсуждают разбивку bounded contexts. Инженер А говорит: «Customer означает одно и то же в Sales и в Customer Support — оба заботятся об одном человеке». Инженер Б говорит: «Но Sales заботится об истории покупок, лид-скоре и менеджере; Support — об открытых тикетах, уровне сервиса и эскалациях — это действительно разные модели». Как определить: один bounded context или два?»

Вспомните перед уходом
  1. 01
    Что такое bounded context и почему одно и то же слово намеренно означает разное в разных контекстах?
  2. 02
    В чём разница между субдоменом и bounded context?
  3. 03
    Что означает «основная единица декомпозиции» применительно к DDD?
Итог

Сущность Order на 47 полей из вступительной истории была не проблемой плохого объектно-ориентированного дизайна. Это была проблема трёх различных моделей — Sales, Billing, Shipping — принудительно втиснутых в один класс, потому что никто не провёл явной границы.

Bounded context решает это, объявляя: внутри этой границы данное слово означает именно вот это, модель согласована, и изменения здесь не требуют координации с той другой командой. Граница языковая (ubiquitous language), модельная (согласованный набор классов и правил) и структурная (закреплена границами пакетов или сервисов в коде).

Bounded contexts — это не то же самое, что субдомены. Субдомены — пространство проблем бизнеса: обнаруживаемое, не проектируемое. Bounded contexts — ваше пространство решений: проектируемое вашей командой на основе субдоменов, топологии команд и сложности модели.

Следующий урок рассматривает ubiquitous language внутри bounded context — почему это инструмент проектирования, а не просто соглашение об именовании, и как дрейф имён является первым сигналом о том, что граница стоит не там.

Практика

Начни сверху. Задачи идут от простого к сложному: вспомнить факт, применить к случаю, затем senior-уровень. Открой, попробуй, потом открой ответ.

вспомнитьприменитьуглубить0 из 4 завершено

Что-то непонятно?

Задай вопрос по этому уроку. Вопросы анонимны и попадают напрямую автору — урок станет лучше.

Примени это

Примени этот урок в реальном проекте.

хоткеи развернуть
поиск
K
пред. пьеса
k
след. пьеса
j
тиры
t
это меню
?
sources3
expand
  1. 01
  2. 02
  3. 03

Trademarks belong to their respective owners. Editorial reference only.