Прямой ответ
Context mapping описывает способы связи между ограниченными контекстами. Связь бывает не только технической — она отражает отношения команд: кто от кого зависит, кто согласовывает изменения, кто подстраивается под чужую модель.
Основные типы связей:
| Тип | Суть | Когда применять |
|---|---|---|
| Партнёрство | Два контекста меняются согласованно | Тесно связанные команды, общий результат |
| Общее ядро | Часть модели разделяется явно | Небольшая общая область при высокой цене дублирования |
| Клиент–поставщик | Поставщик учитывает требования клиента | Внутренняя интеграция с диалогом команд |
| Конформист | Клиент принимает модель поставщика без перевода | Поставщик не готов меняться, модель приемлема |
| Антикоррупционный слой | Клиент транслирует чужую модель в свою | Внешняя или нестабильная модель |
| Открытый хост | Поставщик публикует протокол для многих клиентов | Интеграция с несколькими потребителями |
| Публикуемый язык | Общий формат обмена | Нужен нейтральный формат, понятный всем |
| Раздельные пути | Контексты не интегрируются | Связь не нужна или слишком дорога |
| Большой ком грязи | Границ нет, модель перетекает | Антипаттерн, требующий изоляции |
Практический смысл карты — она делает выбор явным. Без карты связь по умолчанию становится конформизмом: команда копирует чужую модель внутрь, потому что так быстрее, и через год изменение во внешней системе ломает внутреннюю логику.
Наиболее полезный на практике паттерн — антикоррупционный слой. Он изолирует внутреннюю модель от чужой:
class DeliveryAdapter:
"""Переводит модель внешней службы доставки в термины домена."""
def to_shipment(self, external: dict) -> Shipment:
return Shipment(
shipment_id=external["trackingCode"], # их имя -> наше
state=ShipmentState(external["st"]), # их код -> наш enum
promised_at=parse_iso(external["eta_iso"]),
)
Ни один чужой термин не проникает внутрь домена. Если поставщик переименует поле, изменится один адаптер, а не сорок мест в бизнес-логике. О паттерне подробнее — в статье про антикоррупционный слой.
Практика: как применять карту контекстов
1. Рисуйте карту до интеграции. Схема из прямоугольников и подписанных связей занимает час и предотвращает месяцы неявных решений.
2. Подписывайте тип каждой связи. Стрелка без подписи ничего не говорит. «Заказы → Доставка: клиент–поставщик» и «Заказы → Доставка: конформист» — разные обязательства.
3. Не используйте общее ядро без необходимости. Разделённая модель требует согласования каждого изменения. Общее ядро оправдано, когда дублирование дороже согласования.
4. Конформизм — осознанный выбор, а не отсутствие выбора. Принять чужую модель допустимо, если она стабильна и близка к вашей. Ошибкой является конформизм по умолчанию.
5. Антикоррупционный слой обязателен для внешних систем. Внешний поставщик не согласует изменения с вами, поэтому перевод нужен всегда.
6. Открытый хост требует публикуемого языка. Протокол для многих потребителей должен быть документирован и версионирован — см. статью про OpenAPI.
7. Раздельные пути — нормальное решение. Не всякую интеграцию нужно строить. Иногда дублирование небольшой части данных дешевле связи.
Рекомендации:
- Фиксируйте карту в репозитории. Диаграмма, которую никто не обновляет, вводит в заблуждение сильнее, чем её отсутствие.
- Связывайте карту с командами. Тип связи отражает отношения команд: партнёрство невозможно между командами, которые не общаются.
- Пересматривайте при изменении структуры. Смена команды-владельца меняет допустимый тип связи.
- Изолируйте большой ком грязи. При интеграции с legacy-системой её модель не должна проникать внутрь — о постепенной замене в статье про Strangler Fig.
- Согласуйте способ обмена. Синхронный запрос и событие задают разные обязательства по доступности; о событийном обмене — в статье про событийную интеграцию.
Практический сценарий: внешняя CRM продиктовала модель внутри системы
Контекст: интеграция с внешней CRM была выполнена напрямую: объекты CRM сохранялись во внутреннюю базу без перевода. Через полгода CRM изменила схему, и внутренняя логика сломалась в двадцати трёх местах.
- Диагностика. Поиск по коду показал, что имена полей внешней системы использовались в расчётах, валидации и отчётах. Внутренней модели сущности не существовало.
- Причина. Связь была реализована как конформизм по умолчанию: команда приняла чужую модель, чтобы ускорить запуск.
- Оценка. Прямой перенос на новую схему CRM требовал правок во всех найденных местах и не защищал от следующего изменения.
- Действия. (а) Описали внутреннюю модель клиента в терминах предметной области; (б) ввели адаптер, переводящий объекты CRM во внутреннюю модель, и обратный адаптер для выгрузки; (в) заменили прямые обращения к полям CRM на обращения к внутренней модели; (г) добавили контрактные тесты на формат CRM, чтобы изменение схемы обнаруживалось на тесте адаптера; (д) отметили связь на карте как антикоррупционный слой и зафиксировали причину.
- Результат. Последующее изменение схемы CRM потребовало правки одного адаптера и одного теста; внутренняя логика не затрагивалась.
Вывод: тип связи определяет стоимость изменений. Конформизм дешевле в начале и дороже в долгосрочной перспективе; антикоррупционный слой требует дополнительных усилий на старте и окупается при первом изменении внешней системы.
FAQ
Нужна ли карта контекстов в небольшой системе? При двух-трёх контекстах достаточно краткого описания связей. Ценность карты растёт с числом участников и команд.
Чем карта контекстов отличается от диаграммы сервисов? Диаграмма сервисов показывает развёртывание и сетевые связи. Карта контекстов показывает границы моделей и отношения команд, которые не видны на схеме развёртывания.
Можно ли изменить тип связи позже? Да. Переход от конформизма к антикоррупционному слою — обычная практика после накопления опыта работы с чужой моделью.
Что делать, если поставщик не хочет учитывать требования? Выбирать между конформизмом и антикоррупционным слоем. Если модель поставщика нестабильна, перевод обязателен независимо от его готовности к диалогу.
Источники: domainlanguage.com (DDD), Martin Fowler (BoundedContext), InfoQ — ссылки в шапке.