Ein sprachunabhaengiges Konzept, das den Geschaeftsfluss innerhalb von Servicefunktionen in deklarative Bloecke (Sequences) zerlegt.

Das Problem

Bestehende SSOT decken nur das ab, was ausserhalb einer Funktion liegt:

Bestehendes SSOTAbdeckungsbereichFunktionsinneres?
OpenAPIAPI-Routen, Parameter, AntwortschemaX
SQL DDLTabellenstruktur, Indizes, ConstraintsX

Das Innere einer Funktion – der Geschaeftsfluss “Abfrage -> Validierung -> Erstellung -> Antwort” – hat keinen Ort, an dem er deklariert werden kann. Man muss den Implementierungscode lesen, um ihn zu verstehen. Diese Luecke fuellt die Sequence.

SSOT-Hierarchie

SSaC ist die dritte Schicht, die auf bestehenden SSOTs aufbaut.

OpenAPI       → API-Grenze (Routen, Parameter, Antworten)
SQL DDL       → Datengrenze (Tabellen, Constraints, Indizes)
SSaC          → Funktionsinneres (Geschaeftsfluss, Sequence-Deklaration)
─────────────────────────────────────────────────
Implementierungscode → Vom Codegen generiert (Fehlerbehandlung, Boilerplate)

Die obigen drei Schichten uebernehmen die Deklarationen, und der Implementierungscode wird daraus abgeleitet. Der Mensch schreibt nur die Deklarationen.

Kernkonzepte

sequence

Eine deklarative Einheit, die Ausfuehrungsbloecke innerhalb einer Funktion typisiert.

Deklariere nur das What (was zu tun ist), der Codegen uebernimmt das How (wie es getan wird).

10 feste Typen

TypRolleBeispiel
authorizeBerechtigungspruefungRichtlinienabfrage basierend auf Aktion/Ressource/ID
getRessourcenabfrageModel.FindByID(id) -> result
guard nilBeenden wenn Ergebnis nullFehler und Abbruch wenn nicht vorhanden
guard existsBeenden wenn Ergebnis existiertFehler und Abbruch wenn bereits vorhanden
postRessourcenerstellungModel.Create(fields…) -> result
putRessourcenaenderungModel.Update(id, fields…)
deleteRessourcenloeschungModel.Delete(id)
passwordPasswortvergleichHash-Vergleich, Abbruch bei Nichtuebereinstimmung
callExterner AufrufRessourcenoperationen, die nicht als CRUD ausdrueckbar sind; Abbruch bei Fehler
responseAntwort zurueckgebenjson, view, redirect

Da die Typen geschlossen sind, ist Codegenerierung durch typweises Template-Matching moeglich. Dies ist die Grundlage fuer symbolischen Codegen ohne LLMs.

Geltungsbereich von model

Ein model ist nicht auf Datenbanktabellen beschraenkt. Jede Ressource, die per CRUD behandelt werden kann, ist ein model. Datenbankabfragen, Dateipruefungen und externe API-Aufrufe werden alle einheitlich mit get/post/put/delete ausgedrueckt.

Syntaxbeispiel (Go PoC)

Obwohl SSaC ein sprachunabhaengiges Konzept ist, verwendet die Referenzimplementierung Go-Kommentarsyntax. Da die KI Go AST bereits versteht, entstehen keine zusaetzlichen Lernkosten.

Einfach (Erstellung)

// @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) {}

Komplex (Loeschung + Berechtigung + Validierung + externer Aufruf)

// @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) {}

Codegen-Ergebnis

Die aus den obigen Sequence-Deklarationen abgeleitete Implementierung:

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,
    })
}

Token-Kostenvergleich

Sequence-DeklarationImplementierungscode
Zeilenanzahl10-15 Zeilen30-100 Zeilen
Ausgedruckter Inhaltwhat (was zu tun ist)how (wie es getan wird)
FehlerbehandlungKeine (vom Codegen generiert)Vollstaendig enthalten
BibliotheksabhaengigkeitenKeineimport, Initialisierung, Aufrufe
AenderungszeitpunktBei Aenderung des GeschaeftsflussesAutomatische Aktualisierung durch erneuten Codegen

Der Mensch wartet nur die Sequence-Deklarationen. Wenn sich eine Deklaration aendert, wird der Codegen erneut ausgefuehrt und der Implementierungscode ueberschrieben.

Designprinzipien

early return – der gemeinsame Vertrag aller Typen

Dies ist die Voraussetzung, die eine lineare Auflistung von Sequences ermoeglicht. Mit Ausnahme von response beendet jede Sequence bei Fehler sofort die Ausfuehrung. call ist keine Ausnahme. Einfach von oben nach unten lesen, ohne Verzweigung.

TypFehlerbedingung
authorizeKeine Berechtigung -> return
getAbfragefehler -> return
guard nilnull -> return
guard existsExistiert -> return
post/put/deleteDB-Fehler -> return
passwordNichtubereinstimmung -> return
callFehler -> return
responseLetztes (Rueckgabe)

Da call denselben Vertrag wie guard befolgt, muss nach einem call kein separater guard angehaengt werden. Der Codegen generiert automatisch Fehlerpruefung + early return fuer jeden call.

@transaction

Metadaten auf Funktionsebene. Es ist kein Sequence-Typ (unter den 10), sondern eine Deklaration, ob die gesamte Funktion in eine Transaktion eingehuellt werden soll.

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

In Kombination mit der early-return-Struktur ergeben sich die Transaktionsgrenzen natuerlich:

@transaction deklariert
  → guard-Fehler → rollback + return
  → letzte Sequence erfolgreich → commit
  → Fehler aufgetreten → rollback + return

Da es keine Verzweigung gibt, ist die Transaktionsgrenze einheitlich: “die gesamte Funktion”. Es muessen keine begin/end-Positionen angegeben werden, und der Codegen entscheidet allein anhand des Vorhandenseins oder Fehlens von @transaction ueber das Wrapping. Wenn zwei Transaktionen benoetigt werden, bedeutet das zwei Verantwortlichkeiten. Teile die Funktion auf.

call – eine Sonderform von model

call hat dasselbe Wesen wie model. Wenn model Ressourcenoperationen darstellt, die als CRUD (get/post/put/delete) ausdrueckbar sind, dann stellt call Ressourcenoperationen dar, die nicht als CRUD ausdrueckbar sind. Beide rufen Arbeit mit externen Abhaengigkeiten auf und kehren bei Fehler zurueck.

Ressourcenoperationen
  +-- model (CRUD)      → get/post/put/delete
  +-- call (Nicht-CRUD) → component oder func
  • @component – Registrierungsbasiert. Wird befoerdert, wenn ein sich wiederholendes Muster dreimal oder oefter auftritt.
  • @func – Einzigartige Logik. Wird direkt von einem Menschen oder einer KI implementiert.

Komplexe Logik wie Verzweigung mit anschliessendem Zusammenfuehren oder bedingte Verarbeitung in Schleifen erweitert nicht die Sequence, sondern wird an call delegiert. Delegiert, aber der Vertrag bleibt derselbe – Abbruch bei Fehler.

@message

Optionale Metadaten, die jedem Sequence-Typ angehaengt werden koennen. Sie geben die Nachricht an, die dem Benutzer bei einem Fehler uebermittelt wird.

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

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

Wenn @message weggelassen wird, generiert der Codegen automatisch eine Standardnachricht aus Typ + Modellname. Nur bei Bedarf einer benutzerdefinierten Nachricht muss es deklariert werden.

Reine Funktionen existieren nicht in der Serviceschicht

Alle in einer Sequence deklarierten Bloecke sind Operationen mit externen Abhaengigkeiten. Reine Funktionalitaeten wie Formatkonvertierung oder Wertberechnung werden nicht in der Serviceschicht, sondern innerhalb des Models oder der aufgerufenen Funktion verarbeitet. Servicefunktionen uebernehmen nur die Orchestrierung; reine Berechnungen gehoeren dem Aufgerufenen.

Typreferenz

SSaC definiert keine eigenen Typen. Es referenziert Typen, die aus bestehenden SSOT-Quellen abgeleitet werden.

QuelleBeispiel
DB-Schema (SQL DDL)Project, Session
API-Spezifikation (OpenAPI)CreateSessionRequest
SchnittstellendefinitionFileSystem, Cache

Namenskonventionen

KategorieRegelBeispiel
TypPascalCase, aus dem SSOT uebernommenProject, Session
VariablecamelCase, via @result deklariertproject, sessionCount
Request-FeldPascalCase, aus der Anfrage extrahiertProjectID, Command
Reservierte VariablecamelCase, vom Framework bereitgestelltcurrentUser, config

Die unterschiedliche Gross-/Kleinschreibung von Typen und Variablen ermoeglicht es, sie allein anhand der Deklaration zu unterscheiden. project ist eine Variable, Project ist ein Typ.

Spracherweiterung

SSaC ist ein sprachunabhaengiges Konzept. Go ist die Referenzimplementierung (PoC), und aus denselben Sequence-Deklarationen kann Code in anderen Sprachen generiert werden.

Sequence-Deklaration (gemeinsam)
  |
  +-- Go Codegen
  +-- Python Codegen
  +-- TypeScript Codegen
  +-- ...

Die Spezifikation (Typdefinitionen, Syntax, Validierungsregeln) und der Codegen sind getrennt.

Die Go-Referenzimplementierung ist auf GitHub verfügbar.