-
Notifications
You must be signed in to change notification settings - Fork 0
03 Domain and Workflow
FOWOCO의 핵심 Domain은 “누가 어떤 근로자의 어떤 업무를, 어떤 조건으로 다음 단계로 옮겼는가”입니다. 단순 CRUD보다 상태 전이와 증빙·승인이 중요합니다.
| 객체 | 쉬운 설명 | 핵심 관계 |
|---|---|---|
Company |
FOWOCO를 사용하는 사업장 | 모든 업무 데이터의 tenant |
User |
HR·관리자·조회자 | Company에 속하고 Role을 가짐 |
Worker |
업무 대상 외국인근로자 | Company에 속하고 Task·Document를 가짐 |
WorkerDocument |
문서 유형·상태·만료일 metadata | Worker에 속하고 FileReference를 선택적으로 가짐 |
Task |
HR이 실제로 처리할 업무카드 | Worker, TaskType, 상태, version, 담당자 |
ChecklistItem |
완료해야 할 필수 단계 | pinned Workflow Catalog에서 생성된 snapshot |
Approval |
특정 Task 내용 revision에 대한 사람의 결정 | 승인 후 핵심값 수정 시 무효화 |
Evidence |
완료를 증명하는 참조 | Document, File, 외부 제출번호 등 |
WorkerLink |
근로자용 만료 URL | 승인된 특정 Task와 허용 action만 노출 |
AiRun |
AI 요청의 영속 실행 단위 | 여러 TaskCandidate를 가질 수 있음 |
AiAttempt |
같은 AiRun 안의 1회 실행 시도 | 오류·시각·version을 수정하지 않고 보존 |
TaskCandidate |
AI가 제안한 업무카드 후보 | HR이 각각 확정·폐기 |
AuditEvent |
누가 무엇을 했는지 남긴 기록 | append-only |
-
Knowledge Workflow Catalog: 업무 유형별 필수정보·체크리스트·승인·완료 기준.
knowledge저장소가 versioned bundle로 배포합니다. -
AI Agent Pipeline: 자연어를 Intent·Slot·후보로 해석합니다.
ai저장소가 소유합니다. -
Server Task Workflow: 실제 Task의 상태 변경, 권한, 승인, 증빙을 강제합니다.
server저장소가 소유합니다.
Server는 Catalog의 정확한 version을 읽어 Task 생성 시 snapshot을 남기지만 Catalog 원문을 임의 수정하지 않습니다.
stateDiagram-v2
[*] --> DRAFT
DRAFT --> NEEDS_INFO: 필수정보 부족
DRAFT --> READY_FOR_REVIEW: 필수정보 충족
NEEDS_INFO --> READY_FOR_REVIEW: 정보 보완
READY_FOR_REVIEW --> APPROVED: HR 승인
READY_FOR_REVIEW --> DRAFT: 반려·수정 필요
APPROVED --> WAITING_WORKER: 근로자 행동 필요
APPROVED --> WAITING_EXTERNAL: 외부기관 처리 대기
APPROVED --> COMPLETED: 즉시 완료 조건 충족
WAITING_WORKER --> READY_FOR_REVIEW: 응답 후 재검토 필요
WAITING_WORKER --> WAITING_EXTERNAL: 제출 뒤 외부 대기
WAITING_WORKER --> COMPLETED: 증빙·조건 충족
WAITING_EXTERNAL --> READY_FOR_REVIEW: 보완 필요
WAITING_EXTERNAL --> COMPLETED: 외부 처리·증빙 완료
DRAFT --> CANCELLED
NEEDS_INFO --> CANCELLED
READY_FOR_REVIEW --> CANCELLED
APPROVED --> CANCELLED
WAITING_WORKER --> CANCELLED
WAITING_EXTERNAL --> CANCELLED
| 상태 | 초보자를 위한 의미 | 대표 다음 행동 |
|---|---|---|
DRAFT |
막 만든 초안 | 담당자·대상·기한 확인 |
NEEDS_INFO |
필수값이 부족함 | 누락정보 입력 또는 요청 |
READY_FOR_REVIEW |
사람이 검토할 준비 완료 | 편집·승인 요청 |
APPROVED |
특정 version을 HR이 승인 | 근로자 전달·외부 제출 |
WAITING_WORKER |
근로자 확인·응답·파일을 기다림 | Worker Link 상태 확인 |
WAITING_EXTERNAL |
출입국 등 외부기관 결과를 기다림 | 제출 reference·기한 관리 |
COMPLETED |
증빙과 완료 조건 충족 | 수정 대신 후속 Task 생성 |
CANCELLED |
사유와 함께 중단 | 재개 대신 새 Task 검토 |
IN_PROGRESS, FAILED는 Task 상태로 쓰지 않습니다. 진행 중이라는 모호한 상태 대신 무엇을 기다리는지 표현하고, 기술 실패는 AiRun/Event 상태로 관리합니다.
범용 PATCH status=...로 상태를 덮어쓰지 않습니다.
| Domain Command | Authenticated API | 대표 전이 |
|---|---|---|
requestReview(taskId, expectedVersion) |
POST /api/v1/tasks/{taskId}/approval-requests |
DRAFT/NEEDS_INFO → READY_FOR_REVIEW |
approve(taskId, expectedVersion) |
POST /api/v1/tasks/{taskId}/approve |
READY_FOR_REVIEW → APPROVED |
reject(taskId, expectedVersion, reason) |
POST /api/v1/tasks/{taskId}/reject |
READY_FOR_REVIEW → DRAFT |
issueWorkerLink(taskId, expectedVersion) |
POST /api/v1/tasks/{taskId}/worker-link |
APPROVED → WAITING_WORKER |
recordExternalSubmission(taskId, expectedVersion, reference) |
POST /api/v1/tasks/{taskId}/external-submissions |
APPROVED/WAITING_WORKER → WAITING_EXTERNAL |
complete(taskId, expectedVersion, evidenceIds) |
POST /api/v1/tasks/{taskId}/complete |
APPROVED 또는 허용 대기상태 → COMPLETED
|
cancel(taskId, expectedVersion, reason) |
POST /api/v1/tasks/{taskId}/cancel |
비종료 상태 → CANCELLED
|
각 Command는 현재 상태, Actor Role, company, 필수정보, 증빙, optimistic lock version을 검사하고 Activity·Audit을 남깁니다.
반려하면 현재 승인 요청을 종료하고 사유를 Audit에 남긴 뒤 DRAFT로 돌아갑니다. 담당자가 수정하고 다시 승인 요청해야 합니다.
PR #37은 #11의 승인·감사 경계를 다음처럼 구현합니다.
| 저장 객체 | 초보자를 위한 의미 |
|---|---|
ApprovalRequest |
AI 원본, HR 최종본, 변경 field, source version을 승인 시점에 고정 |
ExternalSubmission |
서버가 대신 제출하는 것이 아니라 HR이 제출한 기관·시각·안전한 접수 참조값을 기록 |
Evidence |
완료를 확인할 파일 reference 또는 안전한 메모 |
AuditEvent |
actor·role·action·target·request/trace ID·안전한 요약을 append-only로 기록 |
승인 요청, Task 상태 변경, 전이 이력, 감사 이벤트는 하나의 DB transaction으로 저장합니다. 감사 기록 저장이 실패하면 상태 변경만 남지 않고 함께 되돌아갑니다.
역할은 다음처럼 제한합니다.
-
ADMIN,HR: 승인 요청·승인·반려·제출·증빙·완료 -
VIEWER: 한 Task의 안전한activities조회 -
ADMIN: 자기 사업장의/api/v1/audit-events검색 - 타 사업장 Task·감사는 ID를 알아도
404로 숨김
감사 조회는 AI/HR snapshot 원문, 파일 reference, JWT, Worker Link token을 반환하지 않습니다. 승인 snapshot에 여권·외국인등록·전화·계좌·Secret·전체 Prompt가 섞이면 요청 전체를 거부합니다.
stateDiagram-v2
[*] --> QUEUED
QUEUED --> RUNNING
RUNNING --> SUCCEEDED
RUNNING --> RETRYING: 일시 오류
RETRYING --> RUNNING
RUNNING --> FAILED: 영구 오류 또는 retry 소진
RETRYING --> FAILED
FAILED --> RETRYING: HR 수동 retry
| 상태 | 의미 |
|---|---|
QUEUED |
요청과 실행 event가 DB에 저장됨 |
RUNNING |
AI Runtime 호출 또는 응답 검증 중 |
RETRYING |
제한적 backoff 뒤 재실행 예정 |
SUCCEEDED |
내부 호출과 계약 검증이 끝남 |
FAILED |
자동 진행이 불가능한 기술 실패 |
AI의 업무 판정인 NEEDS_INFO, REVIEW_REQUIRED는 AiRun 상태가 아니라 outcome입니다.
POST /api/v1/ai-runs/{aiRunId}/retry는 실패한 Run 안에 새 AiAttempt를 추가하고 FAILED → RETRYING으로 전이합니다. 이전 attempt의 오류·시각·version은 그대로 보존하며 202 + 같은 aiRunId를 반환합니다. 같은 idempotency key로 retry를 반복해도 새 attempt가 중복 생성되지 않습니다.
입력 예시:
“응웬반A 체류연장 준비하고 여권 사본도 요청해줘”
AI Runtime은 EXPIRY_RENEWAL, DOCUMENT_REQUEST 후보를 각각 반환할 수 있습니다. Server는 두 후보를 저장하되 실제 Task로 자동 등록하지 않습니다.
POST /api/v1/ai-runs/{aiRunId}/candidate-decisions{
"decisions": [
{"candidateId": "candidate-1", "decision": "ACCEPT"},
{"candidateId": "candidate-2", "decision": "DISCARD", "reason": "이미 제출됨"}
]
}-
ACCEPT: 현재 후보 snapshot으로 Task를 한 번만 생성합니다. -
DISCARD: 업무에는 등록하지 않고 폐기 사유와 Actor를 Audit에 남깁니다. - 같은 idempotency key나 같은 후보 확정이 반복돼도 Task가 중복 생성되면 안 됩니다.
MVP 대표 유형:
EXPIRY_RENEWALDOCUMENT_REQUESTPAYROLL_EXPLANATIONEMPLOYMENT_CHANGEWORK_NOTICE
각 Task는 생성 시 사용한 workflow_catalog_version과 체크리스트 snapshot을 저장합니다. 나중에 Knowledge bundle이 바뀌어도 진행 중인 Task의 조건이 몰래 바뀌지 않습니다. 정책상 migration이 필요하면 별도의 명시적 Command와 Audit을 사용합니다.
- AI 후보는 승인할 수 있는 상태가 아니라 검토 대상입니다.
- 승인자는 해당 Company와 Role을 가져야 합니다.
- JPA
version은 동시에 수정한 요청이 서로 덮어쓰지 못하게 합니다. - 승인은
task_id + content_revision + critical_fingerprint + 승인 snapshot에 묶입니다. - 대상자, 기한, 금액, 문서 종류, 안내문처럼 중요한 값을 수정하면 승인을 무효화합니다.
- 승인되지 않은 Task는 Worker Link 발급과 전달이 불가능합니다.
- 완료 조건이 있는 Task는 Evidence 없이
COMPLETED가 될 수 없습니다.
- Task와 중요한 aggregate에
@Version을 사용합니다. - Command에는
expectedVersion을 전달합니다. - API command는 필요한 곳에
Idempotency-Key를 사용합니다. - Event consumer는 at-least-once 전달을 전제로 멱등하게 처리합니다.
- 충돌 시 최신 상태를 덮지 말고
CONCURRENT_MODIFICATION을 반환합니다.
- 허용/금지 상태 전이
- Role 부족과 타 사업장 접근
- 필수정보 부족과
NEEDS_INFO - 승인 후 핵심값 변경 시 승인 무효화
- 승인 전 Worker Link 발급 차단
- Evidence 없는 완료 차단
- 후보 확정 동시 요청에도 Task 하나
- 오래된
expectedVersion거부 - Catalog version 변경 뒤 기존 Task snapshot 유지