Когда разработчик заходит в незнакомый сервис, первый вопрос — «что он вообще делает?». Ответ обычно приходится собирать по кусочкам: читать код, смотреть в базу, расспрашивать коллег. Это теряет время и приводит к ошибкам.
Спецификация — единый документ, который отвечает на этот вопрос. Описывает один 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.
Что входит в корневой файл
Корневой файл описывает контекст в целом. Он состоит из одиннадцати разделов:
| № | Раздел | Что внутри |
|---|---|---|
| 1 | Bounded Context | миссия, субдомен (Core/Supporting/Generic), владелец, таблица агрегатов, что внутри/вне границы, стыки |
| 2 | Интеграции (Context Map) | карта контекстов (mermaid) + таблица рёбер + ссылки на контракты (OpenAPI/AsyncAPI) |
| 3 | Ubiquitous Language | глоссарий контекста; «не путать с» |
| 4 | Роли и доступ | роли, общие ABAC-правила, PII; доступ к операциям — в каждом агрегате |
| 5 | Доменные события | контракт публикуемого языка: агрегат → внешние события → топик |
| 6 | Use Cases | сквозные сценарии карточками ### UC-N |
| 7 | Процессы | Saga/Process Manager: кросс-агрегатные процессы, sequence-диаграммы |
| 8 | UI-спецификация | связь статусов с 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 → Repository | DDD-агрегатов нет (один «модуль»); «Доменная модель» = ER + таблицы; «Доменные события» и «Процессы» пропускаются с пометкой |
| 2 — Use Case Pattern | UseCase + 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); неприменимые разделы помечаются, не пропускаются.
Что почитать дальше
- Кейс маркетплейса — заполненные примеры спек реальных сервисов.
- Уровни зрелости — чем отличаются уровни 1, 2 и 3.
- Стратегические паттерны DDD — границы контекстов и Context Map.
- Настройка скиллов —
ucp-spec-design(по бизнес-описанию),ucp-spec-tier-0(из кода),ucp-spec-review(ревью).