Ограниченные контексты (bounded context, далее кратко BC) — это архитектурное решение под задачи разработки

Есть миф, который никак не умрёт: ваши ограниченные контексты должны отображать бизнес-домены один к одному. Звучит красиво и логично, но это неправда.

Этот миф живёт, потому что:

  • иногда так действительно получается,

  • так могло бы быть в идеальных, но во многом воображаемых условиях,

  • он как бы освобождает от глубокого моделирования и критического анализа вашей организации и программных систем.

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

Краткое напоминание

В предметно-ориентированном проектировании (Domain-Driven Design, DDD) домен — это область деятельности, знаний и экспертизы, в которой работает ваша организация. Например, вы можете быть в финансах, с множеством поддоменов: трейдинг, розничный банкинг, кредитование, продажи, удержание клиентов, плюс бухгалтерия и HR.

Ограниченный контекст (BC) — это граница языка и модели. Внутри этой границы мы используем единый, однозначный язык и одну согласованную модель, чтобы обсуждать и осмыслять задачу.

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

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

Появление софта нарушило эту картину. Внезапно в компании появляются люди, чья экспертиза — не в бизнес-доменах, а в разработке программного обеспечения. Они собраны в отдельный инженерный отдел, но пишут системы для остальных отделов. Иначе говоря, оргструктура компании построена вокруг людей, а не вокруг задач разработки.

В DDD рассуждают так: инженерам нужно разрабатывать, поддерживать и развивать безопасные и производительные системы, которые обслуживают бизнес. Чтобы это делать, инженерам нужно понимать и бизнес-домены, и программные системы. Для этого мы оставляем домены такими, как их видит сама организация, а вот свои ограниченные контексты рисуем так, как нужно нам, чтобы лучше понимать систему. Ограниченные контексты существуют в первую очередь для инженеров и для коммуникации инженеров с предметными экспертами и другими бизнес-функциями.

В идеале мы смотрим, как сама организация «нарезает» домены, и стараемся отразить это в схеме ограниченных контекстов. На самом деле многие сторонники DDD утверждают, что только так и правильно. Но существует столько контрпримеров, что я не понимаю, зачем вообще так настаивать. Представление организации о своих доменах — это не архитектурное решение, поэтому слепо копировать эту структуру в дизайн систем может быть вредно.

Один домен в нескольких ограниченных контекстах

Вот хороший пример. Онлайн-ритейлер считает ценообразование одним из своих ключевых доменов. У него есть два софта. Первый собирает данные с сайтов конкурентов, чтобы находить цены на те же товары, и использует машинное обучение, чтобы реагировать на входные сигналы — такие как спрос и предложение. Вторая система позволяет сотрудникам вручную выставлять цены, настраивать скидки и промокампании, заниматься стратегическим ценообразованием (например, позиционировать одну линейку товаров как более премиальную по сравнению с другой) и психологическим ценообразованием (вроде автоматического превращения 10 € в 9,99 €). Во второй системе также заложены правила, которые выбирают финальную цену между двумя системами и учитывают минимальную маржу.

Почему две системы? Язык программирования, стек технологий и инженерные навыки, которые используются во второй системе, вполне типичны для бизнес-приложений: базы данных, пользовательские интерфейсы, доменная модель… Первая система требует компетенций в машинном обучении, а это уже другая специализация. Плюс к этому одну систему сделали раньше, а потребность во второй возникла позже. То есть необходимость в разных навыках и технологиях, а также исторические причины привели к такому разделению. По-моему, уже этих трёх факторов достаточно, чтобы обосновать разные ограниченные контексты. Инженеры подбирают свои BC под собственные задачи.

Мне кажется, здесь напрашиваются как минимум четыре ограниченных контекста.

Я бы разделил веб-скрейпинг и машинное обучение на два разных BC. Это очень разные технические области. А с точки зрения DDD у них ещё и принципиально разные предметные языки и требования к моделированию, и смешение этих вещей даёт больше путаницы, чем пользы. Если посмотреть на это архитектурно, система машинного обучения получает минимум три вида входных данных: результаты веб-скрейпинга, собственные данные о продажах компании и данные о поставках. В текущем варианте один из этих источников фактически встроен внутрь самого потребителя данных, и это довольно негибко. Разделение этих частей упростит добавление новых источников данных в будущем.

Контекст ручного ценообразования (Manual Pricing) остаётся, но я бы выделил из него часть, основанную на правилах. Логика та же: контекст правил ценообразования получает на вход данные из Manual Pricing, из системы машинного обучения и из системы поставок.

Итак, один домен (ценообразование) с точки зрения организации, но четыре разных языка и модели, а значит — четыре ограниченных контекста, которые лучше отражают потребности инженеров.

Являются ли это на самом деле четырьмя отдельными доменами? Спорный вопрос, в каком-то смысле да. Но организация так на это не смотрит. И в любом случае сейчас в системе есть 2 BC, которые закрывают четыре области, и переход к моему варианту с четырьмя BC может оказаться неоправданно дорогим. В таком случае у нас получится 2 ограниченных контекста на 4 домена — и это тоже может быть вполне разумным выбором.

Вот ещё несколько примеров, которые показывают веские причины не стремиться к отображению 1 к 1.

Стартапы

Теоретически стартап, у которого ещё нет существующих программных систем, — самое удобное место, чтобы сделать отображение доменов и ограниченных контекстов по схеме «один к одному». Вы смотрите на домены, которые нужны стартапу, рисуете под них ограниченные контексты и начинаете разработку.

У стартапов нет роскоши аккуратных границ. Вы ещё только разбираетесь, в каком именно бизнесе на самом деле находитесь, параллельно пишете софт. Поэтому вы запихнёте управление пользователями в платёжную систему просто потому, что у разработчиков нашлось время именно там. Вы выберете ограниченные контексты слишком рано, когда ещё толком их не понимаете. Разнесёте обработку заказов по трём сервисам, потому что каждый из них делался в разные периоды и под разные пивоты. Всё держится на хаках и изоленте. Это не про красивый дизайн системы, это про выживание. Вам почти всегда не хватает людей, навыков, времени и денег.

Нужно найти баланс между тем, чтобы принимать хорошие архитектурные решения, и тем, чтобы делать хоть что-то, что позволит компании дожить до инвестиций или выручки. Со временем потребности меняются, ресурсы появляются, понимание растёт. Тогда уже можно переосмыслить дизайн и перерисовать границы — при условии, что код достаточно гибок, чтобы выдержать такие изменения.

Эксперименты

Есть один способ использования ограниченных контекстов, о котором Эрик Эванс (автор концепции Bounded Context) говорит довольно часто. Если вы изолируете небольшую область в отдельный BC, вы можете использовать её как экспериментальную площадку. Вы пробуете бизнес-идеи или технические решения, получаете знания, не задевая существующие системы, а потом можете просто выкинуть этот кусок.

Допустим, в рамках домена ценообразования у нас есть изолированный BC для ценообразования на основе правил, который имеет небольшой, чётко определённый интерфейс с остальной системой. Мы можем оставить его как есть и создать новый контекст для ценообразования по правилам. Он может быть спроектирован по-другому, более эффективно, использовать другую технологию — например, движок правил, — или опираться на иную модель и язык. Мы можем провести A/B-тест на небольшой части бизнеса, используя простой переключатель или фича-флаг, чтобы направлять запросы либо в существующий BC, либо в экспериментальный.

Если эксперимент удался — мигрируем. Если нет — удаляем. Рисков для основной системы нет.

Зрелые компании

У зрелых компаний программные системы живут годами и десятилетиями. Легаси-система продолжает существовать, потому что приносит больше ценности, чем стоила бы её замена. Переработать всё целиком так, чтобы получился наш идеальный мэппинг 1 к 1, просто нереально.

Редизайн сложен ещё и потому, что значительная часть знаний о системе уже утрачена. Нельзя перепроектировать то, чего толком не понимаешь. (DDD и современное проектирование ПО это признают. У нас есть паттерны, которые помогают изолировать плохо понятные системы от новых компонентов.)

«Любой компонент, который легко заменить, рано или поздно будет заменён компонентом, который заменить трудно». (Закон Сустрика.) Другими словами, достаточно старая система будет сильно сцеплена внутри, и выделять в ней отдельные BC становится всё сложнее.

Решения о том, что улучшать, что заменять, а что оставить как есть, — это часть архитектурного дизайна, который опирается на анализ стоимости и выгод.

Слияния и поглощения

Вот где миф про соответствие 1 к 1 окончательно рассыпается: слияния и поглощения. Допустим, наш ритейлер из примера выше покупает почти идентичного конкурента. Внезапно у нас минимум два ограниченных контекста для ценообразования, два каталога товаров, две бухгалтерские системы — по два экземпляра всего. Для бизнеса при этом по-прежнему существует по одному домену: один «Ценообразование», один «Каталог», одна «Бухгалтерия». А инженеры видят перед собой клубок пересекающихся контекстов, которые каким-то образом нужно заставить работать вместе.

И такая ситуация может оказаться дешевле и эффективнее, чем попытка всё унифицировать! Более реалистичный сценарий: часть мы захотим объединить, а часть оставить как есть.

Бухгалтерия — очень зрелый домен, и можно предположить, что слияние двух компаний для них — привычная рутина. Но, скажем, у конкурента может быть один ограниченный контекст, который одновременно отвечает и за каталог товаров, и за ценообразование. Возможно, одна компания лучше умеет в web scraping, а другая — в машинное обучение. Решения о том, что оставить, от чего мигрировать, что объединить, что разделить и где пройдут новые границы, — всё это смесь бизнес-решений и решений по архитектуре ПО. В итоге у вас получится весьма занятная карта контекстов: лоскутное одеяло из ограниченных контекстов, которые пересекаются с несколькими доменами, и доменов, которые пересекаются с несколькими контекстами. Лучшая архитектура — это та, которая одновременно обслуживает и бизнес-потребности, и потребности инженеров.

Выводы

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

Границы ограниченных контекстов должны быть результатом осознанных архитектурных решений. Инженерам нужно упростить себе работу так, чтобы они могли закрывать потребности бизнеса и в краткосрочной, и в долгосрочной перспективе. Как языковые и модельные границы, ограниченные контексты — отличный инструмент для этого. Если мы позволяем себе быть связанными тем, как организация сама видит свои домены, мы на самом деле не принимаем архитектурных решений. Копирование «реальности» (или чьего-то представления о ней) не является нашей целью. Оно всего лишь удовлетворяет потребность в простых правилах дизайна или красивых схемках системы. («Если схема вашей системы выглядит слишком красиво, эта схема лжёт».)

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

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

Короче говоря, рисуйте границы ограниченных контекстов прагматично, чтобы получать от них пользу.

Upd.: Один из читателей написал мне такой комментарий: «Очень хорошие примеры, они действительно передают смысл этих двух терминов и то, чем они отличаются. В выводах ты мог бы ещё раз подчеркнуть, что ограниченные контексты — это также артефакты старых решений. Признание их существования помогает принимать более качественные решения». Нам нужно моделировать не только желаемое конечное состояние, но и то, как система выглядит сейчас. Если «сказочная» модель показывает идеальные границы Bounded Context, которых нет в реальном коде, инженеры всё равно не будут понимать, куда им можно добавлять или вносить изменения в функциональность.

Осилите программу курса Microservice Architecture? Пройдите входной тест и узнаете
Осилите программу курса Microservice Architecture? Пройдите входной тест и узнаете

Красивые схемы доменов и bounded context’ов легко нарисовать на доске, сложнее — жить с ними в проде, где легаси, десятки БД, давящий AI-хайп и вечные споры аналитиков с архитекторами. Если как раз здесь у вас больше вопросов, чем ответов, можно разложить всё по полочкам на бесплатных демо-уроках:

  • 15 декабря 19:00 — Влияние развития технологий AI на корпоративную архитектуру. Записаться

  • 23 декабря 20:00 — Polyglot Persistence: как современные системы живут с десятками баз данных. Записаться

  • 24 декабря 20:00 — Паттерны микросервисной архитектуры: как системному аналитику говорить с архитектором на одном языкех. Записаться

Комментарии (0)