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

Context mapping

Карта контекстов документирует интеграцию bounded contexts. Паттерны — Shared Kernel, Customer/Supplier, Conformist, ACL, Open Host Service — описывают командные отношения и стратегии трансляции, а не только форму API.

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

Sales-контекст B2B-платформы вызывал сторонний сервис расчёта налогов. У сервиса была своя модель «продуктов» — он называл их «taxable items» с полями commodityCode, taxJurisdiction и lineNetAmount. Sales-контекст имел собственную модель LineItem с NegotiatedPrice и productId. Разработчик, интегрирующий два контекста, пошёл путём наименьшего сопротивления: он использовал объектную модель налогового сервиса напрямую в Sales-контексте. Класс LineItem обзавёлся полем commodityCode. Сервисный метод Quote, вызывавший налоговый сервис, стал возвращать TaxCalculationResult налогового сервиса напрямую в остальной Sales-контекст. Через три месяца весь Sales-код был усеян полями commodityCode и taxJurisdiction, которые не значили ничего ни для одного доменного эксперта Sales. Когда провайдер налогового сервиса изменил API — заменив taxJurisdiction на enum jurisdictionCode — изменение распространилось в основную доменную модель Sales-контекста. Изменение Sales-фичи требовало понимания объектной модели налогового сервиса. Sales-контекст был заражён языком налогового сервиса. Именно это Эванс называет «коррупцией модели через интеграцию». Когда два bounded context интегрируются без явного слоя трансляции, upstream-модель просачивается в downstream-контекст. Ubiquitous language downstream-контекста постепенно замещается языком upstream. Граница контекста растворяется. Карта контекстов — стратегический инструмент, предотвращающий это: диаграмма и набор именованных паттернов, описывающих не только то, как контексты коммуницируют, но какие властные отношения у них есть и какую стратегию трансляции они используют.

Что такое карта контекстов

Карта контекстов — стратегический обзор bounded contexts системы и отношений между ними. Она отвечает на вопросы:

  • Какие контексты существуют?
  • Как каждый контекст интегрируется с другими?
  • Каковы командные отношения в каждой точке интеграции?
  • Какая стратегия трансляции предотвращает коррупцию модели?

Карта контекстов — не архитектурная диаграмма сервисов или API. Это диаграмма команд и моделей. Стрелки на карте контекстов несут метки, описывающие организационные и модельные отношения — Shared Kernel, Customer/Supplier, Conformist, Anticorruption Layer — а не просто «вызывает» или «отправляет событие в».

Карта контекстов B2B-платформы включает: Sales, Billing, Shipping, Product Catalog, Tax Calculation (внешний). Каждая пара контекстов, которые интегрируются, имеет метку отношения. Метка определяет взаимодействие команд и метод трансляции моделей.

Паттерны интеграции

Shared Kernel

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

Используйте, когда: два контекста подлинно разделяют ключевой концепт, который дорого дублировать или транслировать. На B2B-платформе value object Money (сумма + валюта) может быть элементом Shared Kernel — он нужен и Sales, и Billing, он стабилен, и наличие двух отдельных реализаций Money, которые могут тихо расходиться, опасно.

Избегайте для: всего, что эволюционирует с разной скоростью в каждом контексте. Shared Kernel, который одна команда хочет менять, а другая нет, создаёт узкое место координации.

Customer/Supplier

Один контекст (Supplier) производит данные или сервисы, которые потребляет другой (Customer). Модель Supplier определяет интерфейс. Customer адаптируется к ней. Но критично то, что отношения согласовываются: Customer может запрашивать изменения, Supplier их рассматривает, и интерфейс эволюционирует при явной координации.

Это наиболее распространённый паттерн между внутренними командами. На B2B-платформе Product Catalog — Supplier для Sales: Sales потребляет данные каталога. Команда Product Catalog поддерживает API; команда Sales может запрашивать дополнения или изменения; команда Catalog расставляет приоритеты и планирует их.

Властные отношения важны. Если команда Supplier игнорирует нужды команды Customer, команда Customer вынуждена обходиться вокруг модели Supplier — что нередко приводит к коррупции модели в Customer-контексте.

Conformist

Customer-контекст не имеет рычагов влияния на Supplier. Supplier не будет менять интерфейс под нужды Customer. Customer просто соответствует: он принимает модель Supplier как есть, включая её язык, формы объектов и жизненный цикл. Ubiquitous language Customer-контекста подчиняется языку upstream.

Это правильный выбор, когда: Supplier — крупная внешняя система, не заинтересованная в ваших потребностях (SaaS-платформа, сторонний поток данных), И модель Supplier достаточно разумна для прямого использования, И стоимость построения слоя трансляции превышает выгоду от сохранения собственной модели.

B2B-платформа может быть Conformist по отношению к платёжному процессору — если API процессора стабилен и его модель «payment», «charge» и «refund» достаточно чисто отображается на концепты Billing, подчинение может быть прагматичным.

Опасность: как только вы становитесь Conformist, вы импортировали ubiquitous language upstream в свой контекст. Ваша модель теперь эволюционирует в темпе Supplier. Дрейф модели в upstream распространяется напрямую в ваш контекст.

Anticorruption Layer (ACL)

Customer-контекст транслирует между моделью Supplier и своей собственной моделью, защищая свой ubiquitous language. ACL — явный компонент трансляции: он живёт на интеграционной границе, говорит на языке Supplier снаружи и на языке Customer внутри.

Именно этот паттерн должен был использовать Sales-контекст B2B-платформы с налоговым сервисом. ACL транслировал бы: LineItemTaxableItem (при вызове налогового сервиса), TaxCalculationResultQuoteTaxSummary (при возврате результата в Sales). Модель Sales-контекста никогда не видела бы commodityCode или taxJurisdiction. ACL поглощает трансляцию.

ACL — выбор по умолчанию при интеграции с внешними системами, модель которых вы не контролируете и чей язык существенно отличается от вашего.

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

Почему «anti-corruption»? Потому что альтернатива — позволить upstream-модели просочиться в ваш контекст — Эванс описывает как «коррупцию» вашей доменной модели. Паттерн Conformist — осознанный выбор принять эту коррупцию, потому что стоимость построения слоя трансляции не оправдана. ACL — явный отказ от этой коррупции: мы потратим инженерные усилия на поддержку собственной модели, а не позволим языку upstream заменить наш. Стоимость ACL реальна (код трансляции для написания, поддержки и тестирования). Выгода — ваш контекст сохраняет свою модель: она эволюционирует в ответ на ваших доменных экспертов, а не на изменения API вашего upstream.

Open Host Service (OHS) и Published Language (PL)

Когда контекст является Supplier для многих downstream-контекстов, он может формализовать свой интеграционный интерфейс как Open Host Service: чётко определённый протокол, с которым может интегрироваться любой downstream без индивидуальных переговоров. OHS стабилен, версионирован и спроектирован для потребления несколькими потребителями.

Published Language — формальный, хорошо задокументированный формат данных, используемый OHS: обычно версионированная схема событий, публичный API-контракт или стандартный формат. На B2B-платформе контекст Product Catalog может предоставлять Published Language из событий товаров (ProductCreated, ProductPriceChanged, ProductDiscontinued) со стабильной схемой, которую потребляют Sales, Billing и Shipping. Схема версионирована; о критических изменениях сообщается заранее.

Паттерн OHS + PL снижает накладные расходы на координацию в отношениях Customer/Supplier при масштабировании — Supplier не договаривается с каждым потребителем индивидуально; он поддерживает опубликованный контракт.

Чтение карты контекстов: власть и трансляция

Самое важное, что коммуницирует карта контекстов, — не топология API, а властные отношения между командами и стратегия трансляции на каждой границе.

Властные отношения: в Customer/Supplier Supplier имеет рычаги влияния. Если Supplier отзывчив и команды хорошо коммуницируют, Customer может эволюционировать. Если Supplier не отвечает (крупная платформенная команда со многими клиентами), Customer в конечном итоге становится Conformist по умолчанию — он не может получить нужные изменения, поэтому адаптирует свою модель под ограничения Supplier.

Стратегия трансляции: ACL означает «мы транслируем на границе, наша модель защищена». Conformist означает «мы приняли upstream-модель». Shared Kernel означает «мы совместно владеем этой частью». Эти выборы имеют накапливающиеся последствия на протяжении лет. Команда, ставшая Conformist десять лет назад, сейчас имеет внутреннюю модель, глубоко связанную с дизайном API upstream-сервиса образца 2015 года.

lesson.inset.note

Карта контекстов — не единоразовая диаграмма. Её следует обновлять при добавлении новой интеграции, изменении отношения (Customer/Supplier становится Conformist, потому что Supplier перестаёт отвечать) или разделении/слиянии контекста. Карта — живой документ организационных и архитектурных решений команды. Устаревшая карта контекстов так же вредна, как устаревшие комментарии в коде — она описывает систему, которой больше нет.

Викторина

Sales-контекст интегрируется с внутренним Inventory-контекстом. Sales-команда хочет проверять наличие товара перед подтверждением Quote. Inventory-команда говорит: «Используйте наш API напрямую — просто вызовите наш эндпоинт InventoryItem и используйте возвращаемый нами enum InventoryStatus». Тимлид Sales говорит: «Нам нужен ACL». Inventory-команда возражает: «Зачем добавлять накладные расходы трансляции для внутреннего сервиса? Это расточительно». Как принять решение?

Викторина

Product Catalog-контекст публикует событие `ProductPriceChanged` со схемой версии v1. Через шесть месяцев команда Catalog хочет добавить поле `previousPrice` к событию. Sales и Shipping — оба потребители. Что требует от команды Catalog паттерн Open Host Service / Published Language?

Викторина

Два года назад Billing-контекст стал Conformist по отношению к внутреннему Payments-контексту, импортировав типы `PaymentMethod`, `ChargeResult` и `RefundPolicy` Payments-команды напрямую в доменную модель Billing. Payments-команда теперь куплена другой компанией и их сервис заменяется сторонним процессором. Billing-контексту нужно мигрировать. Почему эта миграция значительно сложнее, чем если бы Billing использовал ACL?

Вспомните перед уходом
  1. 01
    Что такое карта контекстов и что она коммуницирует помимо топологии API?
  2. 02
    В чём разница между Conformist и Anticorruption Layer и когда уместен каждый?
  3. 03
    Что такое паттерн Open Host Service и как он снижает накладные расходы координации по сравнению с Customer/Supplier?
Итог

Коррупция модели из вступительной истории — Sales-контекст, заполненный commodityCode и taxJurisdiction от внешнего налогового сервиса — была не случайностью. Это было предсказуемым результатом интеграции без именованной стратегии. Разработчик пошёл путём наименьшего сопротивления: использовать upstream-модель напрямую. Граница bounded context растворилась.

Карта контекстов именует стратегию в каждой точке интеграции. Shared Kernel: обе команды совместно владеют небольшой частью. Customer/Supplier: согласованный интерфейс, downstream имеет рычаги. Conformist: downstream осознанно принимает upstream-модель. ACL: downstream транслирует на границе и защищает свою модель. Open Host Service: upstream публикует стабильный версионированный контракт для нескольких потребителей.

Каждый паттерн описывает командные отношения, а не только технический облик. Ценность ACL не в избегании кода — а в том, что радиус взрыва изменения upstream — один компонент трансляции, а не вся доменная модель. Опасность Conformist не теоретическая — она материализуется когда модель upstream меняется спустя годы.

Карта контекстов рисуется один раз и непрерывно обновляется. Это стратегическая картина, говорящая каждой команде: вот от кого вы зависите, сколько у вас власти и какова ваша стратегия защиты модели.

Практика

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

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

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

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

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

Trademarks belong to their respective owners. Editorial reference only.