상품 스냅숏 고정
구매 당시 BOM과 예약 규칙으로 진료 계획과 순서를 만든다.
Design Specification · 2026-07-26
반복 시술, 패키지, 부분 완료, 장비 고장, 재예약, 통제된 오버부킹과 연장 진료까지 실제 병원의 운영을 하나의 일관된 예약 모델로 표현한다.
Simulation · Default view
업무 담당자가 가장 먼저 알아야 할 것은 “언제 수용량을 쓰는가”이다. 구매는 진료 의무를 만들지만, 실제 자원은 HELD부터 점유하고 CONFIRMED에서 고객 동의와 함께 보호된다.
구매 당시 BOM과 예약 규칙으로 진료 계획과 순서를 만든다.
slot을 제안하되 수용량은 아직 점유하지 않는다.
정책 스냅숏과 함께 짧은 시간 동안 필요한 자원을 보호한다.
고객 동의와 확정 스냅숏을 남기며 정책 변경으로 조용히 재작성하지 않는다.
의료진·공간·장비가 모두 맞는 하나의 시작 시간을 확정한다.
구간 내 도착을 약속하고 현장 흐름이 실제 시작 순서를 조정한다.
정확한 시각 대신 날짜와 우선순위를 약속한다.
새 제안에는 새 정책을 쓰고, 유효한 HELD는 만료까지 보호하며, CONFIRMED는 당시 스냅숏을 유지한다. 약속 변경은 새 제안과 새 동의로 처리한다.
History · Decision provenance
이 문서는 설계를 대체하지 않는다. 아래 이력은 현재 시안이 어떤 범위와 근거에서 나왔는지 추적하게 한다.
규범 Markdown에서 서비스 경계, 상태, 정책 스냅숏, 수용량과 복구 불변식을 결정했다.
구현 계획 시각 문서는 catalog projection과 purchase-to-plan 수렴까지만 현재 실행 범위로 고정한다.
시각 동반 문서 설계와 구현 계획이 로케일, provenance, 검증과 게시 절차를 정의한다.
Current executable slice
sourceAuthority 범위 catalog 스냅숏sourcePurchaseAuthority가 포함된 구매 identity에서 plan·진료 의무·dependency 생성현재 운영 consumer WRITE는 전송과 운영 증거가 갖춰질 때까지 차단한다. 비활성 API는 안전한 FEATURE_DISABLED 계약을 사용한다. 이 HTML의 나머지 모델은 장기 설계이며 Markdown의 현재 실행 경계와 인수 명령이 규범이다.
01 · Bounded Context
상품, 구매, 시술 완료, 환불, 고객 불만을 예약 aggregate에 넣지 않는다. 예약 서비스는 외부 사실을 consume하고 미래 진료 의무와 방문·자원 배정을 관리한다.
상품관리서비스가 BOM과 예약 규칙을, 구매서비스가 계약과 추가 구매를 소유한다.
구매 스냅숏에서 진료 의무를 만들고, 병원별 정책과 가예약·확정·재배정·자원 점유를 소유한다.
시술 원천 사실, 환불 결정, 민원·보상은 각 전문 서비스가 담당한다.
예약은 AppointmentInterrupted, AppointmentDelayExceeded,
RescheduleOffered, AppointmentServiceLevelBreached 같은
객관적 사실만 발행한다. 사과, 상담, 보상과 환불 가능 여부·금액은 CRM·커머스가 판단한다.
| 관심사 | 소유자 | 예약의 책임 |
|---|---|---|
| 상품 BOM·예약 규칙 | 상품관리 | 버전 projection과 plan 스냅숏 |
| 구매·추가 구매 | 구매 | 구매별 새 plan 생성 |
| 실제 시술 완료 | 진료/시술 | 완료 event로 의무 이행·후속 기간 갱신 |
| 환불 판단·금액 | 커머스 | 환불 event로 미래 예약만 취소 |
| 민원·보상 | CRM | SLA 위반과 재예약 사실 제공 |
| SaaS 예약 운영 정책 | 예약 | Tenant 기본값·Clinic override·유효 정책 스냅숏 |
중단은 완료/남은 항목과 새 후보를, 지연은 현재 예상 대기와 선택지를, 제안 만료·거절은 원 예약 보호 여부와 다음 행동을, 환불 취소는 취소된 미래 일정과 남은 방문을 전달한다. 보상·환불 설명은 CRM·커머스가 소유한다.
02 · Domain Model
“예약 그룹”이나 “예약 아래 N회차” 대신 구매별 진료 의무와 실제 방문을 교차 연결한다. N회차는 계획 항목의 표시 메타데이터다.
구매별 불변 BOM 스냅숏
앞으로 이행할 진료 의무와 허용 기간
방문 안의 세부 진료 시도
한 번의 방문과 대표 진료명
한 구매당 하나. 최신 catalog가 바뀌어도 생성 시 스냅숏은 덮어쓰지 않는다.
반복 횟수와 패키지 항목을 전개한 실제 이행 단위다.
완료·중단·연기 상태와 attempt lineage를 가진다.
item별 의료진·장비·공간과 실제 점유 시간을 표현한다.
추가 구매는 언제나 새 plan이다. 다만 같은 환자·병원이고 임상적으로 호환되며 기간이 겹치면
고객 동의를 받은 join proposal로만 서로 다른 plan의 item을 같은 방문에 넣을 수 있다.
plan 자체와 과거 이력은 합치지 않는다. 관계가
Plan → PlannedTreatment ← AppointmentItem → Appointment인 이유다.
03 · Catalog & Revision
IN_PROGRESS와 COMPLETED는 동결04 · SaaS Scheduling Policy
병원별 운영 차이를 코드 분기나 전역 설정으로 숨기지 않는다. 예약서비스가 typed·versioned 정책을 소유하고, 예약·재예약·solver 실행마다 당시의 유효 정책 스냅숏을 남긴다.
확정 약속, hold·동의, 수용량·오버부킹, 우선순위·신뢰도, 재확인, 운영 중단, 영업 연장, 알림·SLA를 독립 정책군으로 관리한다.
DRAFT → validate → impact preview → approve → SCHEDULED/ACTIVE. 컴파일 실패 시 직전 active를 유지한다.
Clinic은 Tenant ceiling과 platform safety guardrail을 완화할 수 없다. 수치 상한은 가장 엄격한 값을 선택한다.
| 예약 상태 | 새 정책 활성화 시 | 보호 원칙 |
|---|---|---|
PROPOSED | 최신 정책으로 대체·재계산 가능 | 아직 고객이 수락하지 않은 후보만 |
HELD | 당시 스냅숏과 자원 점유를 만료까지 보호 | 같은 자원 점유의 확정은 수용량 증가 없이 허용 |
CONFIRMED | 기존 스냅숏 유지 | 새 제안과 고객 동의 없이는 변경 금지 |
IN_PROGRESS / COMPLETED | 과거 정책 근거 보존 | 재계산·재작성 금지 |
기존 약속을 조용히 취소하지 않는다. 신규 hold와 신규 capacity를 늘리는 confirm은 차단하고,
기존 hold의 같은 자원 점유 확정은 허용한다.
POLICY_CAPACITY_DEBT를 경보한 뒤 disruption 또는 고객 동의 기반 재예약으로 해소한다.
긴급 override도 사유·승인자·만료시간을 요구한다.
HOLD_AND_CONSENT처럼 지금 수행하는 처리 흐름은 DECISION_TIME을, capacity·reconfirm·SLA·영업 연장은 실제 진료의 SERVICE_TIME을 사용한다. 이 평가 기준은 병원이 바꾸는 옵션이 아니라 typed schema 계약이며, clinic local time은 timezone과 DST를 검증해 Instant로 저장한다.
Tenant 정책 활성화는 모든 Clinic 스냅숏을 동기 갱신하지 않는다. policyGeneration을 올리고 영향 cache만 무효화한 뒤 lazy compile한다. 새 hold·confirm은 조회 때 받은 generation을 precondition으로 보내며 stale이면 409 POLICY_CHANGED로 최신 후보를 받는다. 기존 hold의 같은 자원 점유 확정은 고정된 스냅숏으로 보호한다.
05 · Booking Lifecycle
구매일은 임상 과정이 아니다. 고객의 희망 날짜·범위를 먼저 사용하고, 입력이 없을 때만 상품의 “구매 후 N일 이내 최초 예약” 규칙으로 후보를 만든다.
병원 또는 시스템의 제안. 수용량을 점유하지 않는다.
만료 시각이 있는 선점형 가예약. 자원을 임시 점유한다.
고객 동의가 완료된 약속. 상품·채널에 따라 바로 진입할 수도 있다.
종료·이탈 경로: 제안 또는 hold 만료는 EXPIRED, 시작 전 취소는
CANCELLED, grace period까지 방문하지 않으면 NO_SHOW다.
시간, 방문 방식, 핵심 의료진, 세부 진료 구성이 실질적으로 바뀌면
version이 있는 RescheduleProposal을 만들고 고객 동의를 받아야 한다.
기존/새 날짜·시간·item·확정 약속 방식·예상 대기, 변경 사유, 응답 기한과 ACCEPT / REJECT / REQUEST_CALL 선택지를 포함한다.
제안 만료 시 후보 자원 점유만 해제하고 원래 확정은 보호한다. 구버전·재사용 nonce·tenant/clinic 불일치는 거부한다.
06 · Partial Fulfillment
장비 고장, 의료진 이탈, 환자 상태, 시간 부족 등 어떤 이유든 방문 전체를 성공 또는 실패로 뭉개지 않는다. 완료한 진료는 보존하고 미이행 항목만 새 attempt로 재예약한다.
INTERRUPTED, fulfillment를 PARTIAL로 기록한다.COMPLETED로 동결한다.INTERRUPTED, 미시작 item은 DEFERRED로 남긴다.AppointmentItem을 만들고 attemptNo와 previousAttemptId를 연결한다.PROPOSED 또는 HELD 방문으로 분리한다.과거 사실과 plan 이행 상태를 유지한다. catalog 변경이나 환불이 다시 쓰지 않는다.
최소 단위로 분리해 새 후보를 만들고, 허용 기간을 벗어나면 BLOCKED_REVIEW로 보낸다.
고객·운영자 화면은 완료 항목, 남은 항목, 임상 기한, 새 방문 필요 여부, 동의 기한을 분리한다. 재예약 거절 시 예약은 사실 event만 발행하고 상담·환불 판단은 CRM·커머스로 넘긴다.
07 · Purchase & Refund
sourcePurchaseId, 새 AppointmentPlanPURCHASE_REFUNDED08 · Disruption & Rescheduling
중단 원인은 다르지만 영향 분석과 복구 원리는 같다. 예약 전체가 아니라 item과 자원 점유 수준에서 영향을 찾고, 같은 방문의 중복 중단을 하나의 case로 합친다.
SlotCalculationService는 hard constraint를 만족하는 후보를 만들고,
Timefold Solver는 여러 예약의 대기·이동·연장·공정성을 함께 비교한다.
이미 유효한 후속 예약은 불필요하게 이동하지 않는다.
중단 event는 tenant/clinic/time-window 기준 30초 debounce하고 case당 10,000 item, 500개 chunk로 제한한다. Solver는 clinic/date/resource group으로 partition하며 사용자 계산 10초, 장애 batch 60초 budget을 넘으면 local repair 후 미해결 항목을 수동 검토로 보낸다.
09 · Priority & Reliability
STANDARD, RETURNING, PRIORITY, CONTRACTED. 제한된 soft weight이며 기존 확정 예약을 빼앗지 않는다.
no-show, 고객 귀책 당일 취소, reconfirm 응답, 정상 방문 누적과 시간 감쇠만 사용한다. 병원 귀책 변경은 제외한다.
“진상 고객” 같은 자유 텍스트는 scheduling 입력으로 금지한다. 안전 사고·폭력 기록은 별도 보안 도메인에서 접근 통제하며 고객 신뢰도 점수와 섞지 않는다.
10 · Capacity Policy
병원별 CAPACITY_AND_OVERBOOKING effective policy에 따라 노쇼와 당일 취소를 고려해 정상 수용량보다 많은 확정 예약을 받을 수 있다.
고객에게 한 약속의 형태와 수용량 초과 정책을 명시적으로 구분한다.
정확한 시작 시각 약속. 시간 준수 우선.
도착 시간대 약속. 도착 후 순차 배정.
날짜와 진료만 약속. 현장 대기·순번 운영.
정상 인력과 자원으로 약속한 SLA 안에 처리할 수 있는 수용량.
병원·진료군·요일·시간대 정책으로 허용한 추가 확정량.
안전과 운영을 위해 어떤 최적화도 넘지 못하는 hard limit.
임상 긴급도 → 확정 약속 방식 → 체크인 시각 → 병원 귀책 공정성 → service tier 순으로 처리한다. 추가 자원을 투입하고 대기를 안내하며 자발적 재예약을 제안한다. 보상과 민원 처리는 예약 외부의 책임이다.
확정 전 확정 약속 방식, 예상 대기 범위, queue 순서 규칙, overflow 가능성, 자발적 재예약 조건을 표시한다. mode별 delay/SLA 기준이 없으면 확정할 수 없다. quota는 기본 0이며 calibration 오차나 대기 SLO 악화 시 자동 축소한다.
11 · Operating Extension
정상 종료 이후 분은 점진적으로 커지는 penalty다. Solver는 연장 비용과 강제 재예약 피해를 비교한다.
자격, 휴게·법정 근로, 장비 안전, 공간 제한, absoluteExtensionLimit은 넘을 수 없다.
12 · Event Contract
ProductCatalogChangedPurchaseCompleted, PurchaseRefunded, PlanCancelledPlanUpdateRequestedTreatmentStarted, TreatmentCompletedClinicCalendarChangedPractitionerUnavailable, EquipmentUnavailableCustomerRescheduleAccepted / RejectedAppointmentPlanCreatedAppointmentProposed / Held / ConfirmedAppointmentInterrupted, AppointmentItemDeferredRescheduleRequired / OfferedAppointmentDelayExceededAppointmentServiceLevelBreachedAppointmentCancelledSchedulingPolicyActivated, EffectiveSchedulingPolicyChanged
모든 외부 event는 event ID, tenant, 원천 aggregate/version을 포함한다.
consume은 inbox 또는 동등한 idempotency 저장소를, publish는 outbox를 사용한다.
AppointmentPlanCreated는 scheduling-owned event ID를 갖고 inbound
event는 causationEventId, 흐름 추적은 correlationId로 보존한다.
mTLS 또는 서명 envelope, issuer/audience, event type별 허용 producer, tenant/clinic, replay window를 검증한다. producer·key ID·algorithm 허용목록이 비어 있으면 시작부터 fail closed한다.
inbox와 side effect를 원자화하고 낮은 version은 무시한다. Foundation의 gap exhaustion은 inbox terminal QUARANTINED이며 broker DLQ 전달을 주장하지 않는다. trust/scope quarantine은 별도 store다. 운영 re-drive는 quarantine ID, 전체 source/catalog identity, actor, release 승인 참조를 고정하고 dry-run/write 감사 로그를 남긴다.
13 · API, Compatibility & Security
PUT /api/{tenant}/clinics/{clinicId}/catalog-sources/{sourceAuthority}/catalog-products/{productId}/versions/{version}. 새 version 201, 동일 hash 200, 동일 version 충돌 409, 낮은 version은 202 STALE_IGNORED.
Tenant와 Clinic version upsert, validate·impact preview·activate, 값별 출처가 있는 유효 정책 조회를 분리한다. stale preview·version hash·expected generation 충돌은 409로 거부한다.
기반 구현 V8은 catalog·plan 기반만 추가하므로 기존 row backfill이 없다. 후속 visit/item 단계에서 scheduling_appointments를 방문 shell로 유지하고 기존 row를 legacy item으로 온라인 backfill한다.
| 기존 상태 | 호환 의미 | 신규 쓰기 |
|---|---|---|
PENDING | legacy provisional | PROPOSED / HELD |
REQUESTED | 확정 대기 | PROPOSED로 해석 |
PENDING_RESCHEDULE | case/제안 대기 | 동의 없는 확정 변경 금지 |
RESCHEDULED | 원 방문 terminal history | 새 방문은 별도 identity |
기존 confirm 요청은 proposal accept로 연결한다. consent가 없는 확정 예약은 그대로 보호하고 409 CONSENT_REQUIRED와 고객의 다음 동의 행동을 반환한다. clinic별 shadow → enforced 전환 동안 기존·신규 endpoint는 같은 stable code를 제공한다.
모든 plan·visit·resource·event와 cross-plan join에 tenant/clinic/patient ownership을 fail closed로 검증한다.
전송·저장 암호화, 최소 projection, 로그·metric 허용목록 redaction, solver 비식별 key, 읽기 감사와 보존 정책을 적용한다.
step-up/고위험 이중 승인과 append-only 감사를 적용하며 safety, 법정 근로, absolute limit, tenant 경계는 override할 수 없다.
Tenant 관리자는 조직 기본값과 소속 Clinic 정책을 관리하고, Clinic 관리자는 자기 병원 override만 수정한다. 조회·수정·preview·활성화 권한을 분리하며 고위험 변경은 step-up과 선택적 이중 승인, 이전·새 version·preview hash·사유가 있는 append-only 감사를 요구한다.
14 · SLO, Recovery & Rollout
| 경로 | 초기 검증 기준 | 초과 시 |
|---|---|---|
| slot search | p95 500ms · p99 1s | query/partition 진단 |
| hold / confirm | p95 300ms · p99 750ms | contention·lock 경보 |
| purchase → plan | p95 30초 | outbox/inbox backlog 경보 |
| disruption → 제안 | 10,000 item에서 p95 5분 | backpressure·chunk·manual review |
| solver | interactive 10초 · batch 60초 | best feasible 또는 대체 경로 |
| policy activation | 결정적 hash와 impact preview 일치 | 직전 active 유지·경보 |
범위 제한 retry → DLQ → 원천 version 확인 → dry-run → event ID/version 지정 re-drive. stuck case는 최신 version으로 멱등 재계산한다.
additive schema → backfill → tenant·clinic 유효 정책 shadow diff → 정확한 tenant/clinic override → consent/disruption → quota 0에서 overbooking 승인 순으로 진행한다. 다른 clinic의 유효값은 바뀌지 않아야 한다.
새 write flag를 끄고 legacy projection read로 돌아가되 생성된 plan/item history는 보존한다. scope 오류·absolute limit 위반·DLQ 급증은 즉시 중단 기준이다.
기반 구현의 local gate와 운영 WRITE gate를 분리한다. schema·contract·benchmark가 통과해도 broker ack/DLQ, 실제 metric/alert, audited flag provider/readback, owner acknowledgement가 없으면 운영 WRITE는 계속 BLOCKED다.
15 · Invariants & Acceptance
위 목록은 장기 설계의 시각 요약이다. 현재 기반 구현 범위, authority-qualified identity, API/event 보안, duplicate/역순 event, migration·SLO와 정확한 실행 명령은 Markdown §20 및 §20.1을 따른다.
slot/policy cache와 online backfill 규모, disruption·solver 품질, quarantine과 운영 alert, reliability profile의 이의제기 절차는 각 후속 실행 계획에서 검증해야 한다. 이 HTML은 요약이며 남은 위험과 운영 활성화 게이트는 Markdown §17–§21이 규범이다.
이 HTML은 설계 이해와 검토를 위한 문서다. 브라우저 안의 클릭은 공식 승인으로 취급하지 않는다.
설계 변경 승인은 채팅/업무 workflow로, 환자의 RescheduleProposal 동의는 proposal version·nonce가 있는 영속 event/명령으로 별도 기록한다.