Языконезависимая концепция, разбивающая бизнес-логику внутри сервисных функций на декларативные блоки (sequence).

Проблема

Существующие SSOT покрывают только то, что находится за пределами функций:

Существующий SSOTОбласть покрытияВнутри функции?
OpenAPIМаршруты API, параметры, схемы ответовX
SQL DDLСтруктура таблиц, индексы, ограниченияX

Внутренняя часть функции — бизнес-поток «запрос -> валидация -> создание -> ответ» — не имеет места для декларативного описания. Его можно понять, только читая код реализации. Именно этот пробел заполняет sequence.

Иерархия SSOT

SSaC — это третий слой, надстроенный над существующими SSOT.

OpenAPI       → API 경계 (경로, 파라미터, 응답)
SQL DDL       → 데이터 경계 (테이블, 제약, 인덱스)
SSaC          → 함수 내부 (비즈니스 흐름, sequence 선언)
─────────────────────────────────────────────────
구현 코드      → 코드젠이 생성 (에러 핸들링, 보일러플레이트)

Три верхних слоя отвечают за декларации, а код реализации выводится из этих деклараций. Человек пишет только декларации.

Основные концепции

sequence

Декларативная единица, типизирующая блоки выполнения внутри функции.

Объявляется только what (что делать), а how (как делать) заполняет codegen.

10 фиксированных типов

ТипРольПример
authorizeПроверка полномочийЗапрос к политике на основе действия/ресурса/ID
getПолучение ресурсаModel.FindByID(id) -> result
guard nilЗавершение, если результат nullОшибка и выход, если не найдено
guard existsЗавершение, если результат существуетОшибка и выход, если уже существует
postСоздание ресурсаModel.Create(fields…) -> result
putОбновление ресурсаModel.Update(id, fields…)
deleteУдаление ресурсаModel.Delete(id)
passwordСравнение пароляСравнение hash, выход при несовпадении
callВнешний вызовОперации с ресурсами, не выражаемые через CRUD, выход при ошибке
responseВозврат ответаjson, view, redirect

Набор типов закрыт, поэтому кодогенерация возможна через сопоставление шаблонов по типам. Это основание для символьной кодогенерации без LLM.

Область применения model

model не ограничивается таблицами БД. Любой ресурс, управляемый через CRUD, является model. Запрос к БД, проверка существования файла, вызов внешнего API — всё одинаково выражается через get/post/put/delete.

Пример синтаксиса (Go PoC)

Концепция языконезависима, но в качестве эталонной реализации используется синтаксис комментариев Go. Поскольку AI уже знает Go AST, дополнительных затрат на обучение нет.

Простой (создание)

// @sequence get
// @model Project.FindByID
// @param ProjectID request
// @result project Project

// @sequence guard nil project
// @message "프로젝트가 존재하지 않습니다"

// @sequence post
// @model Session.Create
// @param ProjectID request
// @param Command   request
// @result session Session

// @sequence response json
// @var session
func CreateSession(w http.ResponseWriter, r *http.Request) {}

Составной (удаление + авторизация + валидация + внешний вызов)

// @sequence authorize
// @action delete
// @resource project
// @id ProjectID

// @sequence get
// @model Project.FindByID
// @param ProjectID request
// @result project Project

// @sequence guard nil project
// @message "프로젝트가 존재하지 않습니다"

// @sequence get
// @model Session.CountByProjectID
// @param ProjectID request
// @result sessionCount int

// @sequence guard exists sessionCount
// @message "하위 세션이 존재하여 삭제할 수 없습니다"

// @sequence call
// @component notification
// @param project.OwnerEmail
// @param "프로젝트가 삭제됩니다"

// @sequence call
// @func cleanupProjectResources
// @param project
// @result cleaned bool

// @sequence delete
// @model Project.Delete
// @param ProjectID request

// @sequence response json
func DeleteProject(w http.ResponseWriter, r *http.Request) {}

Результат кодогенерации

Реализация, производная от приведённых выше деклараций sequence:

func CreateSession(w http.ResponseWriter, r *http.Request) {
    // get
    project, err := projectModel.FindByID(projectID)
    if err != nil {
        http.Error(w, err.Error(), http.StatusInternalServerError)
        return
    }

    // guard nil
    if project == nil {
        http.Error(w, "프로젝트가 존재하지 않습니다", http.StatusNotFound)
        return
    }

    // post
    session, err := sessionModel.Create(projectID, command)
    if err != nil {
        http.Error(w, err.Error(), http.StatusInternalServerError)
        return
    }

    // response json
    json.NewEncoder(w).Encode(map[string]interface{}{
        "session": session,
    })
}

Сравнение стоимости токенов

Декларация sequenceКод реализации
Количество строк10~15 строк30~100 строк
Содержаниеwhat (что делать)how (как делать)
Обработка ошибокОтсутствует (генерируется codegen)Полностью включена
Зависимости от библиотекОтсутствуютimport, инициализация, вызовы
Момент измененияПри изменении бизнес-потокаАвтоматически отражается при повторном запуске codegen

Человек сопровождает только декларацию sequence. При изменении декларации достаточно перезапустить codegen, чтобы перезаписать код реализации.

Принципы проектирования

early return — общий контракт всех типов

Предпосылка, благодаря которой sequence работает как линейный список. За исключением response, каждый sequence немедленно завершает выполнение при ошибке. call — не исключение. Достаточно читать сверху вниз без ветвлений.

ТипУсловие ошибки
authorizeНет полномочий -> return
getОшибка запроса -> return
guard nilnull -> return
guard existsСуществует -> return
post/put/deleteОшибка БД -> return
passwordНесовпадение -> return
callОшибка -> return
responseПоследний (возврат)

Поскольку call следует тому же контракту, что и guard, после call не нужно добавлять отдельный guard. Codegen автоматически генерирует проверку ошибок + early return для каждого call.

@transaction

Метаданные уровня функции. Объявляет не тип sequence (их 10), а необходимость транзакционной обёртки для всей функции.

// @transaction
// @sequence get
// @model Account.FindByID
// @param AccountID request
// @result account Account
// ...

В сочетании со структурой early return границы транзакции определяются естественным образом:

@transaction объявлен
  -> guard ошибка -> rollback + return
  -> последний sequence успешен -> commit
  -> ошибка -> rollback + return

Поскольку ветвлений нет, граница транзакции едина — «вся функция». Нет необходимости указывать позицию begin/end; codegen определяет обёртку исключительно по наличию или отсутствию @transaction. Если нужны две транзакции — значит, у функции две ответственности. Разделите функцию.

call — особая форма model

call имеет ту же сущность, что и model. Если model — это операции над ресурсами, выражаемые через CRUD (get/post/put/delete), то call — операции, не выражаемые через CRUD. Оба вызывают работу с внешними зависимостями и возвращают управление при ошибке.

Операции с ресурсами
  +-- model (CRUD)    -> get/post/put/delete
  +-- call (не-CRUD)  -> component или func
  • @component — регистрационная модель. Повторяющийся паттерн, появившийся 3 и более раз, повышается до component.
  • @func — уникальная логика. Реализуется человеком или AI.

Сложная логика — ветвление с последующим слиянием, условная обработка в циклах и т.п. — не расширяет sequence, а делегируется через call. Делегирование с сохранением контракта — выход при ошибке.

@message

Опциональные метаданные, которые можно прикрепить к любому типу sequence. Задаёт сообщение, передаваемое пользователю при ошибке.

// @sequence guard nil project
// @message "프로젝트가 존재하지 않습니다"

// @sequence post
// @model Session.Create
// @param ProjectID request
// @param Command   request
// @result session Session
// @message "세션 생성에 실패했습니다"

Если @message не указан, codegen автоматически генерирует сообщение по умолчанию из типа + имени model. Объявляйте только тогда, когда нужно пользовательское сообщение.

Чистые функции не существуют в сервисном слое

Все блоки, объявляемые в sequence, — это операции с внешними зависимостями. Чистые функции (преобразование формата, вычисление значений и т.п.) обрабатываются не в сервисном слое, а внутри model или вызываемых функций. Сервисная функция занимается только оркестрацией (orchestration), а чистые вычисления принадлежат вызываемым объектам.

Ссылки на типы

SSaC не определяет собственные типы. Он ссылается на типы, полученные из существующих SSOT.

ИсточникПример
Схема БД (SQL DDL)Project, Session
Спецификация API (OpenAPI)CreateSessionRequest
Определение интерфейсовFileSystem, Cache

Соглашения об именовании

КатегорияПравилоПример
ТипPascalCase, берётся из SSOTProject, Session
ПеременнаяcamelCase, объявляется через @resultproject, sessionCount
Поле requestPascalCase, извлекается из запросаProjectID, Command
Зарезервированная переменнаяcamelCase, предоставляется фреймворкомcurrentUser, config

Разница регистра между типами и переменными позволяет различать их только по декларации. project — переменная, Project — тип.

Языковое расширение

SSaC — языконезависимая концепция. Go является эталонной реализацией (PoC), и из одних и тех же деклараций sequence можно генерировать код на других языках.

sequence 선언 (공통)
  |
  +-- Go 코드젠
  +-- Python 코드젠
  +-- TypeScript 코드젠
  +-- ...

Спецификация (определения типов, синтаксис, правила валидации) и codegen разделены.

Эталонная реализация на Go доступна на GitHub.