backend · advanced · 5d
Архитектура B2B-платформы заказов и биллинга
Спроектируй архитектуру B2B-платформы заказов и биллинга от начала до конца: выдели ограниченные контексты из проблемного пространства, назначь каждому структурный стиль (слоёный, гексагональный или чистый/луковый), реши, где оправданы CQRS, event sourcing и event-driven саги, выбери между модульным монолитом и набором сервисов, явно избегая ловушки распределённого монолита, и обоснуй каждое значимое решение Architecture Decision Record, подкреплённым измеримой фитнес-функцией.
Результат
Письменный архитектурный документ, содержащий: (1) карту контекстов с не менее чем четырьмя ограниченными контекстами и их отношениями (conformist, anti-corruption layer, open host и т.д.); (2) таблицу сопоставления каждого контекста со структурным стилем и письменным обоснованием; (3) раздел стратегии чтения/записи и событий, указывающий, какие контексты используют CQRS, event sourcing и/или саги и почему; (4) решение по декомпозиции — модульный монолит или сервисы — с явным чеклистом против распределённого монолита; (5) не менее трёх ADR в стандартном формате (Title / Status / Context / Decision / Consequences); (6) не менее одной фитнес-функции для наиболее критичного атрибута качества, выраженной в измеримом пороге.
Этапы
0/5 · 0%- 01Определи ограниченные контексты и установи единый язык
Проведи лёгкую сессию event storming (даже соло, на бумаге или в инструменте для доски) для обнаружения доменных событий B2B-платформы заказов и биллинга. Сгруппируй события в не менее четырёх ограниченных контекстов. Для каждого контекста напиши однопараграфный глоссарий единого языка, определяющий ключевые термины так, как они используются внутри этого контекста — обрати внимание на одинаковые слова, означающие разные вещи в разных контекстах (например, «Order» в Ordering vs. Billing). Нарисуй карту контекстов, показывающую отношения upstream/downstream, и пометь каждую границу паттерном интеграции.
Критерии готовности- Существует карта контекстов с не менее чем четырьмя именованными ограниченными контекстами и чётко очерченными границами.
- Каждый ограниченный контекст имеет короткий глоссарий единого языка (не менее пяти ключевых терминов, определённых в диалекте этого контекста).
- Каждая граница контекста на карте помечена паттерном интеграции (conformist, ACL, open host service, shared kernel или published language).
- 02Выбери структурный стиль для каждого ограниченного контекста
Для каждого ограниченного контекста оцени три кандидатуры структурных стилей — слоёный N-tier, гексагональный (ports and adapters) и чистый/луковый — и выбери тот, который подходит для сложности контекста и волатильности его зависимостей. Простой CRUD-ориентированный контекст может быть лучше обслужен слоёным подходом; основной доменный контекст со сложными бизнес-правилами и множеством инфраструктурных адаптеров (база данных, брокер сообщений, внешние API) выиграет больше всего от гексагональной или чистой архитектуры. Напиши таблицу по каждому контексту с колонками: Context, Style Chosen, Rationale (2–3 предложения), Trade-offs Accepted.
Критерии готовности- Каждый ограниченный контекст имеет назначенный структурный стиль в письменной таблице с обоснованием и принятыми компромиссами.
- Для любого контекста, выбирающего гексагональную или чистую архитектуру, документ определяет не менее двух портов (первичного и вторичного) с примерами адаптеров.
- Обоснование хотя бы одного контекста явно объясняет, почему более простой стиль был предпочтён более богатому (или наоборот), со ссылкой на бизнес-сложность контекста.
- 03Определи стратегию чтения/записи и событий
Для каждого ограниченного контекста реши, оправдан ли CQRS (разделение моделей команд и запросов), подходит ли event sourcing (события как система учёта) и должны ли межконтекстные рабочие процессы быть реализованы как event-driven саги. Применяй каждый паттерн только там, где он оправдывает свою сложность. Для контекста Billing явно оцени event sourcing с учётом требований к аудиту и воспроизведению. Для долго выполняющегося рабочего процесса, охватывающего Ordering → Pricing → Billing → Notifications, спроектируй сагу: назови каждый шаг, определи шаг компенсации для каждого режима отказа и реши между хореографией (каждый контекст реагирует на события от предыдущего) и оркестрацией (центральный координатор саги управляет потоком).
Критерии готовности- Письменный раздел указывает, какие контексты используют CQRS, и обосновывает это решение; контексты, не использующие CQRS, имеют однострочное объяснение того, почему дополнительная сложность не оправдана.
- Оценка event sourcing для Billing задокументирована: таблица сравнивает event sourcing и state-based хранилище по не менее чем трём критериям (аудируемость, сложность запросов, стоимость хранения, возможность воспроизведения).
- Межконтекстная сага спроектирована: каждый шаг назван, его шаг компенсации определён, решение между хореографией и оркестрацией принято и обосновано.
- 04Выбери декомпозицию и избегай распределённого монолита
Прими конкретное решение о деплойменте: запустить платформу как модульный монолит (все ограниченные контексты в одном деплойменте с сильными модульными границами, обеспечиваемыми системой сборки) или разделить на независимые сервисы с первого дня. Явно примени чеклист распределённого монолита: общая база данных через границы контекстов? синхронная runtime-связанность между контекстами? совместные пайплайны деплоя? Если хоть что-то из этого присутствует, а ты всё равно называешь это микросервисами — у тебя распределённый монолит. Запиши решение как структурированный аргумент: вариант A (модульный монолит), вариант B (сервисы), решение и конкретные условия, при которых ты перешёл бы от A к B или признал бы B ошибкой.
Критерии готовности- Документ содержит заполненный чеклист распределённого монолита (общая БД, синхронные межконтекстные вызовы, совместный деплой) с явным pass/fail по каждому критерию.
- Решение о деплойменте зафиксировано (модульный монолит или сервисы) с письменным обоснованием, ссылающимся на предположения о размере команды, масштабе трафика и операционной зрелости.
- Условия для перехода от выбранной декомпозиции к альтернативной записаны как конкретные триггеры (например, 'когда каденция выпуска одного контекста расходится с другими', 'когда размер команды превышает N').
- 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 и напиши фитнес-функцию, предотвращающую публикацию невверсионированных событий.