open atlas
← Все проекты

backend · advanced · 5d

Архитектура B2B-платформы заказов и биллинга

Спроектируй архитектуру B2B-платформы заказов и биллинга от начала до конца: выдели ограниченные контексты из проблемного пространства, назначь каждому структурный стиль (слоёный, гексагональный или чистый/луковый), реши, где оправданы CQRS, event sourcing и event-driven саги, выбери между модульным монолитом и набором сервисов, явно избегая ловушки распределённого монолита, и обоснуй каждое значимое решение Architecture Decision Record, подкреплённым измеримой фитнес-функцией.

Это капстоун трека architecture-patterns. Ты не будешь писать код — ты создашь архитектуру для реалистичной B2B-платформы, которая обрабатывает заказы, применяет правила ценообразования, выставляет счета клиентам и отслеживает платежи. Платформа является сквозным примером, используемым в треке, и здесь ты проектируешь её целиком. Начни с доменного исследования: определи не менее четырёх ограниченных контекстов (например, Ordering, Pricing, Billing, Notifications) с помощью event storming или аналогичной техники и установи единый язык (ubiquitous language) для каждого. Нарисуй карту контекстов, показывающую интеграцию между ними — обрати внимание на отношения upstream/downstream и выбери паттерн интеграции для каждой границы (conformist, anti-corruption layer, open host service, published language). Далее назначь структурный стиль каждому контексту. Не каждый контекст заслуживает гексагональной или чистой архитектуры — простой read-only контекст Notifications может быть отлично реализован как слоёный CRUD-сервис, тогда как ключевые контексты Ordering и Billing являются сильными кандидатами для ports-and-adapters, чтобы их доменная логика была полностью изолирована от инфраструктуры. Для Ordering и Billing реши, оправдан ли CQRS: если модели чтения и записи существенно расходятся (запросы отчётности против command-heavy операций), CQRS является естественным выбором. Оцени event sourcing для Billing — требование к аудиту делает его сильным кандидатом, но честно взвесь операционные затраты. Смоделируй межконтекстные рабочие процессы (например, order placed → pricing applied → invoice generated → payment collected) как event-driven саги с явными шагами компенсации и задокументируй режимы отказов. Для декомпозиции сделай конкретную рекомендацию: начать как модульный монолит или сразу разделить на сервисы? Примени чеклист распределённого монолита — если ты выбираешь сервисы, но всё ещё разделяешь базу данных, синхронно вызываешь через границы или деплоишь вместе, у тебя распределённый монолит и нужно исправить дизайн. Наконец, напиши три или более ADR для твоих наиболее значимых решений (например, «Использовать event sourcing для Billing», «Держать Ordering и Billing в одном деплойменте», «Использовать anti-corruption layer между унаследованной ERP и контекстом Ordering»). Для каждого ключевого атрибута качества (например, аудируемость, доступность, задержка на 99-м перцентиле) определи фитнес-функцию: конкретную, автоматизированную или полуавтоматизированную проверку, которая скажет тебе, отклоняется ли архитектура от задуманного.

Результат

Письменный архитектурный документ, содержащий: (1) карту контекстов с не менее чем четырьмя ограниченными контекстами и их отношениями (conformist, anti-corruption layer, open host и т.д.); (2) таблицу сопоставления каждого контекста со структурным стилем и письменным обоснованием; (3) раздел стратегии чтения/записи и событий, указывающий, какие контексты используют CQRS, event sourcing и/или саги и почему; (4) решение по декомпозиции — модульный монолит или сервисы — с явным чеклистом против распределённого монолита; (5) не менее трёх ADR в стандартном формате (Title / Status / Context / Decision / Consequences); (6) не менее одной фитнес-функции для наиболее критичного атрибута качества, выраженной в измеримом пороге.

Этапы

0/5 · 0%
  1. 01Определи ограниченные контексты и установи единый язык

    Проведи лёгкую сессию event storming (даже соло, на бумаге или в инструменте для доски) для обнаружения доменных событий B2B-платформы заказов и биллинга. Сгруппируй события в не менее четырёх ограниченных контекстов. Для каждого контекста напиши однопараграфный глоссарий единого языка, определяющий ключевые термины так, как они используются внутри этого контекста — обрати внимание на одинаковые слова, означающие разные вещи в разных контекстах (например, «Order» в Ordering vs. Billing). Нарисуй карту контекстов, показывающую отношения upstream/downstream, и пометь каждую границу паттерном интеграции.

    Критерии готовности
    • Существует карта контекстов с не менее чем четырьмя именованными ограниченными контекстами и чётко очерченными границами.
    • Каждый ограниченный контекст имеет короткий глоссарий единого языка (не менее пяти ключевых терминов, определённых в диалекте этого контекста).
    • Каждая граница контекста на карте помечена паттерном интеграции (conformist, ACL, open host service, shared kernel или published language).
  2. 02Выбери структурный стиль для каждого ограниченного контекста

    Для каждого ограниченного контекста оцени три кандидатуры структурных стилей — слоёный N-tier, гексагональный (ports and adapters) и чистый/луковый — и выбери тот, который подходит для сложности контекста и волатильности его зависимостей. Простой CRUD-ориентированный контекст может быть лучше обслужен слоёным подходом; основной доменный контекст со сложными бизнес-правилами и множеством инфраструктурных адаптеров (база данных, брокер сообщений, внешние API) выиграет больше всего от гексагональной или чистой архитектуры. Напиши таблицу по каждому контексту с колонками: Context, Style Chosen, Rationale (2–3 предложения), Trade-offs Accepted.

    Критерии готовности
    • Каждый ограниченный контекст имеет назначенный структурный стиль в письменной таблице с обоснованием и принятыми компромиссами.
    • Для любого контекста, выбирающего гексагональную или чистую архитектуру, документ определяет не менее двух портов (первичного и вторичного) с примерами адаптеров.
    • Обоснование хотя бы одного контекста явно объясняет, почему более простой стиль был предпочтён более богатому (или наоборот), со ссылкой на бизнес-сложность контекста.
  3. 03Определи стратегию чтения/записи и событий

    Для каждого ограниченного контекста реши, оправдан ли CQRS (разделение моделей команд и запросов), подходит ли event sourcing (события как система учёта) и должны ли межконтекстные рабочие процессы быть реализованы как event-driven саги. Применяй каждый паттерн только там, где он оправдывает свою сложность. Для контекста Billing явно оцени event sourcing с учётом требований к аудиту и воспроизведению. Для долго выполняющегося рабочего процесса, охватывающего Ordering → Pricing → Billing → Notifications, спроектируй сагу: назови каждый шаг, определи шаг компенсации для каждого режима отказа и реши между хореографией (каждый контекст реагирует на события от предыдущего) и оркестрацией (центральный координатор саги управляет потоком).

    Критерии готовности
    • Письменный раздел указывает, какие контексты используют CQRS, и обосновывает это решение; контексты, не использующие CQRS, имеют однострочное объяснение того, почему дополнительная сложность не оправдана.
    • Оценка event sourcing для Billing задокументирована: таблица сравнивает event sourcing и state-based хранилище по не менее чем трём критериям (аудируемость, сложность запросов, стоимость хранения, возможность воспроизведения).
    • Межконтекстная сага спроектирована: каждый шаг назван, его шаг компенсации определён, решение между хореографией и оркестрацией принято и обосновано.
  4. 04Выбери декомпозицию и избегай распределённого монолита

    Прими конкретное решение о деплойменте: запустить платформу как модульный монолит (все ограниченные контексты в одном деплойменте с сильными модульными границами, обеспечиваемыми системой сборки) или разделить на независимые сервисы с первого дня. Явно примени чеклист распределённого монолита: общая база данных через границы контекстов? синхронная runtime-связанность между контекстами? совместные пайплайны деплоя? Если хоть что-то из этого присутствует, а ты всё равно называешь это микросервисами — у тебя распределённый монолит. Запиши решение как структурированный аргумент: вариант A (модульный монолит), вариант B (сервисы), решение и конкретные условия, при которых ты перешёл бы от A к B или признал бы B ошибкой.

    Критерии готовности
    • Документ содержит заполненный чеклист распределённого монолита (общая БД, синхронные межконтекстные вызовы, совместный деплой) с явным pass/fail по каждому критерию.
    • Решение о деплойменте зафиксировано (модульный монолит или сервисы) с письменным обоснованием, ссылающимся на предположения о размере команды, масштабе трафика и операционной зрелости.
    • Условия для перехода от выбранной декомпозиции к альтернативной записаны как конкретные триггеры (например, 'когда каденция выпуска одного контекста расходится с другими', 'когда размер команды превышает N').
  5. 05Обоснуй через ADR и фитнес-функции

    Напиши не менее трёх Architecture Decision Records в стандартном формате (Title / Status / Context / Decision / Consequences) для трёх наиболее значимых решений в твоей архитектуре — примеры: использовать ли event sourcing для Billing, где разместить anti-corruption layer между платформой и внешней ERP, начинать ли как модульный монолит. Для наиболее критичного атрибута качества платформы (выбери один: аудируемость, доступность или p99 задержка потока размещения заказа) определи фитнес-функцию: конкретный тест или метрику, которую можно запускать автоматически или полуавтоматически и чей сбой указывает на то, что архитектура отклонилась от желаемого уровня качества. Чётко сформулируй порог (например, 'каждое событие биллинга должно быть воспроизводимо в пределах 1 секунды от его оригинальной метки времени'; 'ни один модуль в контексте Ordering не должен импортировать из внутренних пакетов модуля Billing').

    Критерии готовности
    • Написаны не менее трёх ADR, каждый с заполненными пятью стандартными секциями (Title, Status, Context, Decision, Consequences).
    • Определена не менее одной фитнес-функции с точным, измеримым порогом и описанием того, как она будет проверяться (автоматический тест, CI-гейт, алерт мониторинга или каденция ручного аудита).
    • ADR и фитнес-функции упоминаются в общем архитектурном документе, образуя связный след обоснования от требований → проектных решений → обеспечения качества.

Рубрика

Джуниор Миддл Сеньор
Качество границ ограниченных контекстов Четыре контекста названы и отображены на карте, но границы следуют организационной структуре или интуиции, а не доменным событиям; термины перетекают между контекстами без явного слоя трансляции. Границы возникают из event-storming сессии; у каждого контекста есть глоссарий единого языка, а карта контекстов маркирует каждое отношение паттерном интеграции (conformist, ACL, open host и т.д.). Ты можешь объяснить, почему «Order» означает разное в Ordering и Billing, и показать anti-corruption layer, предотвращающий семантическую утечку; карта контекстов отражает асимметрию власти upstream/downstream, и каждая граница обоснована как наиболее дешёвый шов для независимого изменения.
Структурный стиль и направление зависимостей Каждый контекст получает ярлык стиля (слоёный, гексагональный, чистый) и краткое замечание, но обоснование общее («гексагональный хорош для тестируемости»), а не привязано к конкретной волатильности этого контекста. Таблица по контекстам показывает конкретные порты и адаптеры для гексагональных решений, называет, какие зависимости волатильны и почему, и объясняет хотя бы один случай, когда более простой стиль предпочтён, потому что контексту не хватает доменной сложности. Стрелки зависимостей в каждом гексагональном контексте направлены внутрь — ни один инфраструктурный тип не появляется в доменном объекте; ты называешь конкретный риск связанности, которого избегаешь (например, ORM-сущность проникает в доменную логику, тип брокера сообщений в команде саги), и фитнес-функцию, которая поймает нарушение в CI.
Событийная стратегия и покрытие режимов отказа саги CQRS и event sourcing применяются ко всем контекстам, потому что звучат убедительно; межконтекстный поток изображён как последовательность только счастливого пути без определённых шагов компенсации. CQRS применяется только там, где модели чтения и записи существенно расходятся; event sourcing для Billing оценивается относительно state-based хранилища с таблицей сравнения; сага называет каждый шаг и его компенсацию, а выбор между хореографией и оркестрацией сформулирован с обоснованием. Ты называешь операционный налог event sourcing (перестройка проекций, каденция снапшотов, скорость роста хранилища) наряду с его преимуществом для аудита, и можешь указать радиус поражения, когда сам шаг компенсации саги завершается неудачей — например, счёт уже отправлен, когда шаг оплаты истекает по таймауту, — и стратегию обработки последствий.
Решение по декомпозиции и уход от распределённого монолита «Микросервисы» выбраны, потому что это современно; чеклист (общая БД, синхронные межконтекстные вызовы, совместный деплой) не применяется, так что проект несёт все недостатки распределения без какой-либо автономии. Чеклист распределённого монолита заполнен с pass/fail по каждому критерию; решение о деплойменте (модульный монолит или сервисы) ссылается на конкретные допущения о размере команды, трафике и операционной зрелости; триггеры для миграции записаны как измеримые условия. Ты определяешь тот единственный ограниченный контекст, чья каденция выпуска, требование к масштабированию или командное владение, скорее всего, первыми нарушат допущение монолита, указываешь последовательность извлечения, избегающую дробления одним махом, и называешь интеграционный тест, который поймает связанность через общую базу данных до начала извлечения.
Эталонный разбор (спойлер)

Почему ограниченные контексты: bounded context — это единица лингвистической согласованности. Две модели, вынужденные делить схему или иерархию классов, должны соглашаться по каждому термину навсегда; ограниченный контекст позволяет каждой команде владеть своей моделью и переводить на границе, делая каждый контекст независимо изменяемым. Anti-corruption layer — ключевой паттерн, когда upstream-модель вам не подконтрольна.

Правило направления зависимостей: в гексагональной и чистой архитектуре зависимости направлены внутрь — к домену, — никогда наружу к инфраструктуре. Нарушение этого делает домен нетестируемым без базы данных и связывает цикл выпуска бизнес-логики с циклом выпуска фреймворков. Классический режим отказа — ORM-сущность, используемая как доменный объект: изменения схемы ломают бизнес-правила.

Event sourcing для Billing: главная польза — полный воспроизводимый журнал аудита, каждое изменение состояния — это факт в логе. Главная цена — сложность запросов: чтение текущего состояния требует воспроизведения или проекции событий, а проекции нужно перестраивать при изменении их схемы. Стратегия снапшотов (сохранять контрольную точку состояния каждые N событий) ограничивает стоимость воспроизведения, но добавляет операционную нагрузку. Применяй event sourcing там, где аудит и воспроизведение — реальные требования, а не по умолчанию.

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

Сделай по-сеньорски

  • Расширь карту контекстов, включив унаследованную ERP-систему как upstream-контекст, и спроектируй полный anti-corruption layer: определи логику трансляции между моделью данных ERP и единым языком платформы, укажи, как обрабатывать изменения схемы в ERP без каскадных поломок, и напиши ADR для ACL.
  • Спроектируй расширение мультитенантной архитектуры для платформы: реши, является ли мультитенантность сквозной проблемой или ограниченным контекстом, укажи стратегию изоляции данных (общая схема с tenant_id, схема на тенанта или база данных на тенанта) и напиши фитнес-функцию, обеспечивающую изоляцию данных тенанта в CI.
  • Смоделируй схему событий платформы как published language: спроектируй стратегию версионирования доменных событий (версия в типе события, версионирование конверта или реестр схем), укажи путь обновления при необходимости breaking change и напиши фитнес-функцию, предотвращающую публикацию невверсионированных событий.

Навыки

bounded-contextsubiquitous-languagecontext-mappinghexagonal-architectureports-and-adaptersclean-architecturecqrsevent-sourcingevent-drivensaga-patternmodular-monolithanti-corruption-layeradrfitness-functionstradeoff-analysisdecomposition

Рекомендуемый стек

DDD / Bounded ContextsHexagonal ArchitectureClean / Onion ArchitectureCQRSEvent SourcingEvent-Driven (Sagas)Modular MonolithADRs

Материалы