קונספט בלתי תלוי שפה המפרק את הזרימה העסקית בתוך פונקציות שירות לבלוקים הצהרתיים (sequences).

הבעיה

SSOT קיימים מכסים רק את מה שמחוץ לפונקציה:

SSOT קייםטווח כיסויבתוך הפונקציה?
OpenAPIנתיבי API, פרמטרים, סכמת תגובהX
SQL DDLמבנה טבלאות, אינדקסים, אילוציםX

פנים הפונקציה – הזרימה העסקית “שאילתה -> אימות -> יצירה -> תגובה” – אין מקום להצהיר עליה. ניתן להבין אותה רק על ידי קריאת קוד המימוש. ה-sequence ממלא את החלל הזה.

היררכיית SSOT

SSaC היא השכבה השלישית הנבנית מעל SSOT קיימים.

OpenAPI       → גבול API (נתיבים, פרמטרים, תגובות)
SQL DDL       → גבול נתונים (טבלאות, אילוצים, אינדקסים)
SSaC          → פנים הפונקציה (זרימה עסקית, הצהרת sequence)
─────────────────────────────────────────────────
קוד מימוש      → נוצר על ידי codegen (טיפול בשגיאות, boilerplate)

שלוש השכבות שלמעלה אחראיות על ההצהרות, וקוד המימוש נגזר מהן. האדם כותב רק את ההצהרות.

קונספטים מרכזיים

sequence

יחידת הצהרה שמטפסת בלוקי ביצוע בתוך פונקציה.

הצהר רק את ה-what (מה לעשות), וה-codegen ימלא את ה-how (איך לעשות).

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

מכיוון שהטיפוסים סגורים, יצירת קוד באמצעות התאמת תבניות לפי טיפוס אפשרית. זהו הבסיס ל-codegen סימבולי ללא LLM.

היקף ה-model

model אינו מוגבל לטבלאות בסיס נתונים. כל משאב שניתן לטפל בו באמצעות CRUD הוא model. שאילתות בסיס נתונים, בדיקות קיום קבצים וקריאות API חיצוניות – הכל מבוטא באופן אחיד באמצעות get/post/put/delete.

דוגמת תחביר (Go PoC)

למרות ש-SSaC הוא קונספט בלתי תלוי שפה, מימוש הייחוס משתמש בתחביר הערות 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) {}

תוצאת ה-codegen

המימוש הנגזר מהצהרות ה-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 – החוזה המשותף לכל הטיפוסים

זוהי ההנחה שמאפשרת ל-sequences לפעול כרשימה ליניארית. למעט response, כל sequence יוצא מיידית בכישלון. call אינו חריג. פשוט קרא מלמעלה למטה ללא הסתעפות.

טיפוסתנאי כישלון
authorizeאין הרשאה -> return
getשגיאת שאילתה -> return
guard nilnull -> return
guard existsקיים -> return
post/put/deleteשגיאת DB -> return
passwordאי-התאמה -> return
callכישלון -> return
responseאחרון (החזרה)

מכיוון ש-call עוקב אחר אותו חוזה כמו guard, אין צורך להוסיף guard נפרד אחרי call. ה-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 – מבוסס רישום. מקודם כאשר תבנית חוזרת מופיעה שלוש פעמים או יותר.
  • @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 מייצר אוטומטית הודעת ברירת מחדל מטיפוס + שם מודל. יש להצהיר רק כאשר נדרשת הודעה מותאמת אישית.

פונקציות טהורות אינן קיימות בשכבת השירות

כל הבלוקים המוצהרים ב-sequence הם פעולות עם תלויות חיצוניות. פונקציונליות טהורה כגון המרת פורמט או חישוב ערכים מטופלת לא בשכבת השירות אלא בתוך ה-model או בתוך הפונקציה הנקראת. פונקציות שירות מבצעות רק תיאום (orchestration); חישוב טהור שייך לצד הנקרא.

הפניות לטיפוסים

SSaC אינו מגדיר טיפוסים משלו. הוא מפנה לטיפוסים הנגזרים ממקורות SSOT קיימים.

מקורדוגמה
סכמת DB (SQL DDL)Project, Session
מפרט API (OpenAPI)CreateSessionRequest
הגדרות ממשקFileSystem, Cache

מוסכמות שמות

קטגוריהכללדוגמה
טיפוסPascalCase, נלקח מ-SSOTProject, Session
משתנהcamelCase, מוצהר באמצעות @resultproject, sessionCount
שדה requestPascalCase, מחולץ מהבקשהProjectID, Command
משתנה שמורcamelCase, מסופק על ידי ה-frameworkcurrentUser, config

ההבדל ברישיות בין טיפוסים למשתנים מאפשר להבחין ביניהם מההצהרה בלבד. project הוא משתנה, Project הוא טיפוס.

הרחבה לשפות נוספות

SSaC הוא קונספט בלתי תלוי שפה. Go הוא מימוש הייחוס (PoC), ומאותן הצהרות sequence ניתן לייצר קוד בשפות אחרות.

הצהרת sequence (משותפת)
  |
  +-- Go codegen
  +-- Python codegen
  +-- TypeScript codegen
  +-- ...

המפרט (הגדרות טיפוסים, תחביר, כללי אימות) וה-codegen מופרדים.

מימוש הייחוס ב-Go זמין ב-GitHub.