Prompt**Issue-007에 따라 구현된 결과물에 대해
아키텍처 및 스키마와 처리 구조를 비롯한 모든 단계의 시각적 다이어그램들을 작성해줘.**
OutputISSUE-007 결과물을 단계별(아키텍처 → 식별자 정합 → 데이터 모델 → 모듈 구조 → 운영 워크플로우 → 동작 시퀀스 → 라우트 합성)로 시각화한다. 모든 다이어그램은 Mermaid.
콘텐츠(SoT)와 운영 DB가 분리되고, DB는 식별 레지스트리만 둔다. 표시 메타는 manifest에서 읽는다.
flowchart TB
subgraph CP["콘텐츠 평면 (SoT — Git MDX)"]
direction LR
C1["content/<course>/course.mdx"]
C2["content/<course>/<cohort>/cohort.mdx"]
C3["content/<course>/<cohort>/*.mdx (lesson/assignment)"]
end
subgraph BUILD["빌드 (결정성)"]
B1["build-manifest<br/>path-name(key) 검증 + cohortMeta 추출"]
B2["lib/generated/content/index.json<br/><course>/<cohort>.json"]
end
subgraph DBP["운영 DB 평면 (Prisma / Postgres)"]
D1["Course / Cohort<br/>(식별 레지스트리 — key only)"]
D2["Student / Enrollment / CohortLessonVisibility"]
end
subgraph DOMAIN["도메인 (lib/domain/enrollment.ts)"]
M1["enrollStudents / unenroll"]
M2["setLessonVisibility"]
M3["resolveCohortId / findCohortByKeys"]
M4["EnrollmentRepository + LessonRegistry"]
end
subgraph CONS["소비자"]
U1["admin CLI<br/>--course/--cohort key"]
U2["db:sync-from-content"]
U3["라우트 /c/[course]/[cohort]/... (ISSUE-009~011)"]
end
CP --> B1 --> B2
B2 -- "표시 메타(title/기간) 읽기" --> DOMAIN
B2 -- "course/cohort key upsert" --> U2 --> D1
D1 --- D2
DBP --> DOMAIN
DOMAIN --> U1
DOMAIN --> U3
style CP fill:#eef7ee,stroke:#4a7
style BUILD fill:#eef7ee,stroke:#4a7
style DBP fill:#eef2fb,stroke:#47a
같은 key 문자열이 콘텐츠 폴더·DB·라우트 세 곳에서 동일하게 쓰인다. 빌드/CHECK/sync가 이 불변식을 강제한다.
flowchart LR
subgraph GIT["Git 콘텐츠"]
F["content/<course>/<cohort>/"]
end
subgraph DB["DB"]
K["Course.key / Cohort.key<br/>@@unique([courseId, key])"]
end
subgraph RT["라우트"]
R["/c/[course]/[cohort]"]
end
F == "동일 문자열" ==> K
K == "동일 문자열" ==> R
R == "동일 문자열" ==> F
G1["build-manifest: 폴더명 path-name 검증"] -.강제.-> F
G2["DB CHECK: key ~ '^[a-z0-9][a-z0-9-]*$'"] -.강제.-> K
G3["db:sync-from-content: content→DB upsert"] -.정합.-> K
규칙: 소문자·숫자·하이픈만 허용. cohort key는 코스 내에서만 유일하므로 라우트는 [course]/[cohort] 두 세그먼트로 모호성 없이 해석된다.
erDiagram
Course ||--o{ Cohort : "contains (courseId FK)"
Student ||--o{ Enrollment : enrolls
Cohort ||--o{ Enrollment : includes
Cohort ||--o{ CohortLessonVisibility : controls
Lesson_MDX ||..o{ CohortLessonVisibility : "lessonId (논리참조, FK 아님)"
Course {
string id PK
string key UK "content 폴더 / [course] 라우트"
datetime createdAt
}
Cohort {
string id PK
string courseId FK
string key "UK(courseId, key)"
datetime createdAt
datetime updatedAt
}
Student {
string id PK
string email UK "lowercase+trim (DB 트리거)"
string name
datetime createdAt
datetime updatedAt
}
Enrollment {
string id PK
string studentId FK
string cohortId FK
datetime createdAt
}
CohortLessonVisibility {
string id PK
string cohortId FK
string lessonId "Git lsn_ 논리참조"
boolean visible
datetime createdAt
datetime updatedAt
}
Lesson_MDX {
string lessonId "front-matter id (lsn_), cohort마다 복제"
}
인덱스: Enrollment @@unique([studentId, cohortId]) + @@index([cohortId]); CohortLessonVisibility @@unique([cohortId, lessonId]). 복합 unique의 leading prefix가 단독 질의를 커버하므로 courseId/studentId 단독 인덱스는 없고, cohort마다 복제되는 lessonId 단독 인덱스도 두지 않는다.