קונספט בלתי תלוי שפה המפרק את הזרימה העסקית בתוך פונקציות שירות לבלוקים הצהרתיים (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 nil | null -> 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, נלקח מ-SSOT | Project, Session |
| משתנה | camelCase, מוצהר באמצעות @result | project, sessionCount |
| שדה request | PascalCase, מחולץ מהבקשה | ProjectID, Command |
| משתנה שמור | camelCase, מסופק על ידי ה-framework | currentUser, config |
ההבדל ברישיות בין טיפוסים למשתנים מאפשר להבחין ביניהם מההצהרה בלבד. project הוא משתנה, Project הוא טיפוס.
הרחבה לשפות נוספות
SSaC הוא קונספט בלתי תלוי שפה. Go הוא מימוש הייחוס (PoC), ומאותן הצהרות sequence ניתן לייצר קוד בשפות אחרות.
הצהרת sequence (משותפת)
|
+-- Go codegen
+-- Python codegen
+-- TypeScript codegen
+-- ...
המפרט (הגדרות טיפוסים, תחביר, כללי אימות) וה-codegen מופרדים.
מימוש הייחוס ב-Go זמין ב-GitHub.