← назад к разделу

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

Спецификация — единый документ, который отвечает на этот вопрос. Описывает один Bounded Context в доменных терминах: что умеет сервис, какие у него роли, какие события он публикует, как устроен жизненный цикл сущностей. Бизнес-аналитик, архитектор и разработчик заполняют её совместно; AI-агент использует как источник памяти и генерирует по ней код.

Почему не просто README

README описывает «как запустить». Спецификация описывает «что это такое и почему так устроено».

Без спеки одна и та же вещь называется по-разному в коде, в задачах и на созвонах. Новый разработчик не знает, где граница сервиса — что он должен делать, а что нет. AI-агент не может сгенерировать корректный код, не зная бизнес-правил.

Спецификация решает три проблемы:

  • фиксирует единый язык — одни термины в коде, документах и разговорах;
  • задаёт границу сервиса — что внутри, что снаружи, что на стыке;
  • становится источником для генерации кода, тестов и диаграмм.

Принципы формата

Прежде чем смотреть на структуру, важно понять несколько правил, на которых она держится.

Единица спеки — Bounded Context, не агрегат. Один контекст = одна спека. Если в контексте несколько агрегатов, спека разбивается на корневой файл (секции уровня контекста) и по файлу на агрегат. Разбивать один контекст на несколько спек нельзя: Ubiquitous Language, Context Map, роли — свойства всего контекста.

Домен без техники. Все секции, кроме «Технической реализации», пишутся в доменных терминах. Фреймворки, аннотации, схема базы данных, топики, стек — только в разделе «Техническая реализация». Так спека остаётся понятной бизнесу и не устаревает при смене технологий.

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

Карточки + строгие таблицы. Команды, запросы, use cases — карточки ### Имя с размеченными полями: читаемо человеку, парсится агентом. Структурные секции (доступ, события, интеграции) — таблицы.

Раскладка файлов

Спека живёт в docs/spec/ и всегда делится на два уровня:

docs/spec/
  <service>-spec.md          # корень: секции уровня контекста
  aggregates/
    <aggregate>.md           # по файлу на агрегат: секции уровня агрегата

Агрегат — всегда в отдельном файле aggregates/<name>.md, даже если он один. На Уровне 1–2, где DDD-агрегатов ещё нет, в этот файл кладут центральную сущность контекста (product.md, notification.md). Структура «корень + aggregates/» единая во всех спеках.

В начало каждого файла добавляют минимальный frontmatter для идентификации чанка при индексации. Для корня — context, bounded-context, level. Для файла агрегата — context, aggregate, level.

Что входит в корневой файл

Корневой файл описывает контекст в целом. Он состоит из одиннадцати разделов:

РазделЧто внутри
1Bounded Contextмиссия, субдомен (Core/Supporting/Generic), владелец, таблица агрегатов, что внутри/вне границы, стыки
2Интеграции (Context Map)карта контекстов (mermaid) + таблица рёбер + ссылки на контракты (OpenAPI/AsyncAPI)
3Ubiquitous Languageглоссарий контекста; «не путать с»
4Роли и доступроли, общие ABAC-правила, PII; доступ к операциям — в каждом агрегате
5Доменные событияконтракт публикуемого языка: агрегат → внешние события → топик
6Use Casesсквозные сценарии карточками ### UC-N
7ПроцессыSaga/Process Manager: кросс-агрегатные процессы, sequence-диаграммы
8UI-спецификациясвязь статусов с UI, тексты ошибок для пользователя
9Критерии приёмкиGiven / When / Then
10Нефункциональные требованияпроизводительность, доступность, согласованность, безопасность, наблюдаемость — целевые значения без инструментов
11Техническая реализацияединственный технический раздел: контейнеры C2, стек, схема БД (ER + индексы)

Что входит в файл агрегата

Каждый агрегат описывается в семи разделах:

РазделЧто внутри
1Доменная модельагрегат-корень + сущности, value objects (через инвариант), class-диаграмма
2Жизненный циклстатусы + матрица переходов (команды, события, политики/таймауты) + state-диаграмма
3Доступматрица «операция × роль» + ABAC
4Бизнес-правиласписок BR-<префикс>NN с типом, командой и кодом ошибки
5Командыкарточки: Переход · Вход · Предусловия · Логика · Эмитит · Ошибки
6Доменные событиятаблица «событие — триггер — scope — подписчики»
7Запросыкарточки: Вопрос · Параметры · Возвращает · Логика (источник чтения, фильтр, согласованность)

Несколько важных конвенций

Value Objects описываются через инвариант. Не через поля и типы (это зона базы данных), а через то, что они защищают. Для sum-типов — варианты: Discount: Percentage | Fixed.

Бизнес-правила кодируются с префиксом агрегата. BR-O01, BR-D01 — чтобы коды не пересекались между агрегатами.

Команды в «Командах» — только публичные. Реакции на события и политики (таймауты) — не отдельные карточки. Они уже отражены в матрице переходов и «Доменных событиях».

Каталога ошибок нет. Коды ошибок — в «Командах» (поле «Ошибки») и «Бизнес-правилах»; тексты для пользователя — в «UI»; HTTP-маппинг — артефакт API, не спеки.

Кросс-агрегатные ссылки — по ID, без внешних ключей. <other>_id — логическая ссылка; это отражается и в class-диаграммах, и в ER.

Уровень зрелости — сколько заполнять

Спека не требует одинаковой глубины для всех сервисов. Глубина определяется уровнем зрелости сервиса:

УровеньАрхитектураЧто меняется в спеке
0 — As-isреверс из кодаснимок «как есть»; not-declared для пробелов; точка отсчёта для миграции
1 — СлоёныйController → Service → RepositoryDDD-агрегатов нет (один «модуль»); «Доменная модель» = ER + таблицы; «Доменные события» и «Процессы» пропускаются с пометкой
2 — Use Case PatternUseCase + Handlerкоманды = UseCase-классы; события — если реально публикуются; CQRS + Read Model — опция уровня
3 — DDD + Hexagonalагрегаты, доменные события, ports/adaptersполная глубина, разбивка по агрегатам

Раздел, неприменимый на текущем уровне, не пропускают молча — пишут заголовок и однострочную пометку: «Не применимо на Уровне 1».

Уровень 0 — as-is из кода. Это особый режим: спеку по существующему сервису без бизнес-брифа генерирует скилл ucp-spec-tier-0, читая код, миграции и конфигурацию. Он ставит level: 0, не выдумывает агрегаты там, где код без доменной логики, отсутствующие данные помечает литералом not-declared. Это точка отсчёта для перехода на нужный уровень.

Коротко

  • Спека описывает один Bounded Context в доменных терминах; бизнес-правила, роли, события, жизненный цикл.
  • Файлы делятся на корень (уровень контекста) и aggregates/<name>.md (уровень агрегата); структура единая для всех уровней зрелости.
  • Всё, что не «Техническая реализация», пишется без упоминания фреймворков, аннотаций и схемы базы данных.
  • Команды, запросы, use cases — карточки ### Имя; структурные секции — нормализованные таблицы.
  • Бизнес-правила кодируются с префиксом агрегата (BR-O01); каждый факт — в одном месте.
  • Глубина спеки = уровень зрелости сервиса (0–3); неприменимые разделы помечаются, не пропускаются.

Что почитать дальше