Перейти к содержимому
Skein
← Все проекты

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).
    Самопроверка

    Покажи артефакт этапа 'bounded-contexts-and-ubiquitous-language' и объясни ключевой компромисс или режим отказа который он закрывает. Senior-ревьюер проверяет что deliverable измерен (не просто 'работает') и компромисс указан с числами.

  2. 02Выбери структурный стиль для каждого ограниченного контекста

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

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

    Покажи артефакт этапа 'structural-style-per-context' и объясни ключевой компромисс или режим отказа который он закрывает. Senior-ревьюер проверяет что deliverable измерен (не просто 'работает') и компромисс указан с числами.

  3. 03Определи стратегию чтения/записи и событий

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

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

    Покажи артефакт этапа 'read-write-and-event-strategy' и объясни ключевой компромисс или режим отказа который он закрывает. Senior-ревьюер проверяет что deliverable измерен (не просто 'работает') и компромисс указан с числами.

  4. 04Выбери декомпозицию и избегай распределённого монолита

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

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

    Покажи артефакт этапа 'decomposition-avoid-distributed-monolith' и объясни ключевой компромисс или режим отказа который он закрывает. Senior-ревьюер проверяет что deliverable измерен (не просто 'работает') и компромисс указан с числами.

  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 и фитнес-функции упоминаются в общем архитектурном документе, образуя связный след обоснования от требований → проектных решений → обеспечения качества.
    Самопроверка

    Покажи артефакт этапа 'adrs-and-fitness-functions' и объясни ключевой компромисс или режим отказа который он закрывает. Senior-ревьюер проверяет что deliverable измерен (не просто 'работает') и компромисс указан с числами.

Стартер

fallowlone/skein-projects

projects/architecture-patterns-platform

Открыть на GitHub ↗
  • README.md
  • artifact/architecture.json
  • src/architecture.ts
  • test/architecture.test.ts
Забрать только этот проект npx degit fallowlone/skein-projects/projects/architecture-patterns-platform architecture-patterns-platform

Реализуй заглушки, затем гоняй тесты, пока не позеленеют: bun test

Форкни репозиторий и запушь свою работу — workflow grade прогонит тесты и статические проверки на твоих раннерах.

Рубрика

Джуниор Миддл Сеньор
Качество границ ограниченных контекстов Четыре контекста названы и отображены на карте, но границы следуют организационной структуре или интуиции, а не доменным событиям; термины перетекают между контекстами без явного слоя трансляции. Границы возникают из 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

Материалы