[설계 2] 이벤트 상품은 어떤 예약을 만드는가

이벤트 상품은 “한 번 방문하는 상품”이라고 설명하기 쉽습니다. 하지만 예약 서비스가 처리해야 하는 일은 상품을 판매하는 일이 아니라, 그 구매가 어떤 진료 의무와 예약으로 이어지는지 재현하는 일입니다.
환자 A가 이벤트 상품을 구매했다고 해보겠습니다. A가 결제한 순간 병원 달력의 특정 시간과 의사가 자동으로 확정되는 것은 아닙니다. 먼저 상품 기준 데이터와 구매 기준 데이터를 확인하고, 예약 가능한 조건을 계산한 다음, 고객이 제안에 동의해야 비로소 확정 예약이 생깁니다.
구매 사실 ≠ 예약 가능 조건 ≠ 확정 예약
이번 글은 이벤트 상품의 최초 예약을 환자 A의 시간축으로 따라갑니다. N회 상품과 패키지 상품은 같은 경계가 왜 필요한지 보여 주는 비교로만 사용합니다. 반복 회차 전체 계산, 패키지 전환, 노쇼 페널티, VIP 우선 예약은 각자의 운영·상품 정책을 다루는 다음 범위로 남겨 둡니다.
이벤트 상품을 샀는데 왜 아직 예약이 아닌가
섹션 제목: “이벤트 상품을 샀는데 왜 아직 예약이 아닌가”환자 A에게는 세 가지 상품 구매가 차례로 발생할 수 있습니다.
| 시점 | 환자 A의 행위 또는 이벤트 | 상품·구매의 의미 | 예약 서비스가 먼저 보존하는 것 |
|---|---|---|---|
| T1 | 이벤트 상품을 구매한다 | 한 번 사용할 수 있는 상품 계약 | 구매 ID와 구매 당시 상품 버전(version) |
| T2 | N회 방문 상품을 추가 구매한다 | 여러 회차로 이행할 예약 의무 | 회차별 PlannedTreatment와 반복 간격 |
| T3 | 여러 치료 항목의 패키지를 구매한다 | 항목·선후 조건·자원 요구가 있는 실행 BOM | 항목별 버전(version) 출처와 의존성(dependency) |
| T4 | 원하는 날짜를 요청하거나 요청하지 않는다 | 고객의 예약 선호 입력 | 불변 BookingPreferenceSnapshot |
| T5 | 후보 시간에 동의한다 | 특정 시간·항목·자원에 대한 예약 | CONFIRMED 상태와 정책 기준 정보 |
| T6 | 내원하고 처치받는다 | 예약 사실과 임상 사실이 이어짐 | 내원·완료 이벤트(event)와 미이행 항목의 상태 |
이 표에서 핵심은 세 구매를 하나의 Appointment 행으로 합치지 않는다는 점입니다. 이벤트 상품의 한
번 사용, N회 상품의 세 번째 회차, 패키지의 특정 항목은 남은 의무와 선후 조건이 다릅니다. 같은 방문에서
여러 상품의 항목을 함께 처리할 수는 있지만, 방문에 합의된 AppointmentItem과 각 구매의
AppointmentPlan은 계속 별도로 추적해야 합니다.
구매는 환자가 무엇을 사용할 계약을 맺었는지에 대한 상업적 사실입니다. AppointmentPlan은 그 계약을
예약 서비스가 어떤 실행 의무로 해석했는지에 대한 계획입니다. 확정 예약은 특정 시간과 항목, 필요한 자원을
이행하기로 합의한 결과입니다. 세 가지를 같은 레코드로 취급하면 상품 변경이나 일정 변경이 과거 예약을
조용히 덮어쓰게 됩니다.
상품 기준 데이터가 예약 서비스에 들어오는 최소 계약
섹션 제목: “상품 기준 데이터가 예약 서비스에 들어오는 최소 계약”예약 서비스가 상품명을 읽고 “이 정도면 30분 예약”이라고 추측해서는 안 됩니다. 상품 관리 서비스는 예약에 필요한 기준 데이터를 버전으로 발행하고, 예약 서비스는 그 정의를 구매 시점의 불변 입력으로 보존합니다.
현재 ProductCatalogDefinition에는 다음과 같은 예약 해석 정보가 포함됩니다.
| 기준 데이터 | 예약 서비스가 해석하는 의미 |
|---|---|
catalogVersion과 schemaVersion | 어떤 상품 계약과 decoder(해석기)로 해석했는가 |
items의 repeatCount | 몇 개의 PlannedTreatment를 만들 수 있는가 |
durationMinutes | 회차마다 예상되는 자원 점유 시간 |
minimumIntervalDays, preferredIntervalDays, maximumIntervalDays | 반복 회차 사이의 필수 제약(hard)·선호 제약(soft) 간격 |
| 담당자·장비·진료실(room) 유형 | 후보 시간에 필요한 역량과 자원 |
dependencies | 선행 회차가 끝나야 가능한 후행 회차 |
initialBookingRule | 고객 희망 일정이 없을 때 최초 제안을 계산하는 제한된 대체 규칙(fallback) |
상품 이름이나 가격은 이 계약을 대신할 수 없습니다. sourceAuthority는 상품 기준 데이터를 발행한 기준 데이터
원본을 가리키는 식별자이고, catalogVersion은 그 원본이 발행한 특정 불변 revision입니다. 이 필드를
접근 제어 정보로 읽지 않는 이유도 여기에 있습니다. 이 문맥은 인증·인가가 아니라 “어느 서비스의
기준 데이터를 믿고 해석하는가”를 말합니다.
구매 이벤트에는 구매 기준 데이터 원본과 구매 ID가 함께 들어와야 합니다. 소스의
PurchaseCompletedEvent가 전달되면 예약 서비스는 테넌트·병원·구매 기준 데이터 원본·구매 ID 범위에서 이미
처리한 구매인지 확인하고, 아직 계획이 없을 때만 상품 기준 정보를 사용해 계획을 만듭니다. 같은 구매
이벤트를 다시 받았다고 새 구매로 추측하지 않습니다.
구매 사실에서 AppointmentPlan으로
섹션 제목: “구매 사실에서 AppointmentPlan으로”현재 구현에서 구매 한 건은 하나의 AppointmentPlan으로 확장됩니다. 계획은 다음 출처 이력을
남깁니다.
sourcePurchaseAuthority와sourcePurchaseId: 구매 사실을 발행한 기준 데이터 원본과 원본 구매 식별자catalogSourceAuthority,productId,catalogVersion: 어떤 상품 기준 데이터를 사용했는가catalogPayloadHash: 재생 시 같은 상품 기준 정보인지 확인할 수 있는 해시(hash)bookingPreference: 구매 시점에 제출된 고객 희망 일정의 불변 기준 정보PlannedTreatment: BOM 항목의 반복 회차와 소요 시간·자원·간격TreatmentDependency: 선행·후행 회차 사이의 방향성 제약
상품 BOM은 예약 서비스가 임의로 다시 정의하는 목록이 아닙니다. 예약 서비스는 검증된 기준 정보를 읽어 실행 계획으로 확장하고, 그 결과를 다시 재현할 수 있도록 보존합니다. 그래서 상품 기준 데이터가 나중에 바뀌어도 이미 생성된 계획의 과거 항목을 최신 카탈로그로 다시 전개하지 않습니다.
PurchaseCompleted └─> AppointmentPlan └─> PlannedTreatment 1..N ── dependency/DAG ▲ │ fulfills / attemptsAppointment ──> AppointmentItem 1..N ──> ResourceAllocation 1..N이 그림에서 아래쪽 Appointment는 실제 방문 단위입니다. 한 방문에 여러 계획에서 온 AppointmentItem을 묶을 수 있지만, 그 항목이 어느 구매의 어떤 PlannedTreatment를 이행하는지는 계속 남아 있어야 합니다. 자원 배정은 고객이 예약에 동의한 뒤 기록되는 예약 사실이고, 구매 이벤트를 받은 순간 자동으로 자원을 점유했다는 뜻이 아닙니다.
희망 일정과 최초 제안 대체 규칙(fallback)을 분리하기
섹션 제목: “희망 일정과 최초 제안 대체 규칙(fallback)을 분리하기”고객이 “다음 주 화요일 오전”을 요청했다고 해서 그 요청이 확정 시간은 아닙니다. 현재
BookingPreferenceSnapshot은 다음 네 가지 입력을 불변 값으로 보존합니다.
| 입력 | 의미 |
|---|---|
ExactDateTime | 명시한 현지 날짜·시각을 병원 시간대와 UTC 기준 시각으로 정규화한 선호 |
DateRange | 허용 가능한 병원 현지 날짜 범위 |
PreferredWeekdaysAndWindows | 허용 요일과 현지 시간대 범위 |
NotProvided | 고객이 희망 일정을 제공하지 않았다는 명시적인 표시 |
NotProvided일 때만 상품의 initialBookingRule을 대체 규칙(fallback)으로 검토합니다. 예를 들어
WithinDaysAfterPurchase(maximumDays)는 원본 구매일 이후 제한된 기간 안에 최초 가예약 제안을 만들 수
있다는 규칙입니다. 이 규칙은 자동 확정을 허용하지 않고, 고객이 직접 제출한 희망 일정을 덮어쓰지도
않습니다.
여기서 구매 후 최초 제안 기한과 상품 이용 만료일을 혼동하면 안 됩니다. 전자는 예약 서비스가
처음 후보를 제안할 수 있는 계산 규칙이고, 후자는 상품 관리부가 정하는 이용 계약입니다. 현재
ProductCatalogDefinition에는 전자를 표현하는 initialBookingRule이 있지만, 상품 이용 만료일을
자동으로 의미하는 필드는 없습니다.
후보 시간에서 확정 예약까지
섹션 제목: “후보 시간에서 확정 예약까지”후보 계산은 상품 기준 데이터와 병원 운영 기준 데이터를 함께 읽는 단계입니다. 예상 소요 시간, 필요한 담당자· 장비·진료실(room), 반복 간격, 선행 조건을 만족하더라도 해당 병원의 업무시간과 수용량이 맞지 않으면 후보가 되지 않습니다.
현재 원본에는 계획·희망 입력·예약 정책을 표현하는 모델이 있고, 예약 설계는 고객 요청을 잠정 상태(provisional)로 시작하는 흐름을 정의합니다. 따라서 아래 표에서 “현재 구현”과 “승인된 설계”를 나누어 읽어야 합니다.
| 단계 | 상태 | 환자 A에게 생기는 업무 의미 | 상태 표지 |
|---|---|---|---|
| 후보가 계산됨 | candidate | 상품을 사용할 수 있는 조건과 특정 시간 후보가 모두 계산됨 | 승인된 설계 |
| 제안이 생성됨 | PROPOSED | 고객이 검토할 수 있는 시간·항목·자원 조합 | 승인된 설계 |
| 자원을 잠시 보류함 | HELD | 정책에 따라 짧은 보류(hold)를 둔 후보. 아직 고객 동의 전 | 승인된 설계 |
| 고객이 제시된 조건에 동의함 | CONFIRMED | 병원과 환자가 특정 예약을 확정함 | 승인된 설계 |
| 계획 항목이 실제 이행으로 이어짐 | SCHEDULED·IN_PROGRESS·COMPLETED | 예약과 임상 완료가 별도 상태로 진행됨 | 현재 계획 모델 |
PROPOSED와 HELD는 고객에게 보여 줄 후보 또는 잠정 수용량을 표현합니다. 고객이 다른 시간을 원하면
새 제안을 계산해야 하고, 이미 CONFIRMED인 예약을 조용히 옮길 수 없습니다. 확정 예약을 변경하려면 기존
예약을 유지한 채 새 제안을 만들고 고객의 새 동의를 받는 별도 흐름이 필요합니다.

예외는 같은 예약의 실패가 아니다
섹션 제목: “예외는 같은 예약의 실패가 아니다”예약 서비스가 예외를 하나의 FAILED 값으로만 남기면 상담 서비스와 운영자가 다음 작업을 판단할 수 없습니다. 환자 A의 같은 이벤트 상품이라도 아래 상황은 서로 다른 사실입니다.
| 상황 | 예약 서비스가 보존하는 사실 | 다음 업무 |
|---|---|---|
| 후보 시간이 없음 | 계산 조건, 계산 시점, 실패 사유, 재검토 가능 여부 | 조건을 바꾼 후보·대체 시간 제안 또는 상담 handoff |
PROPOSED·HELD 만료 | 만료된 제안과 보류(hold)를 확정 예약으로 승격하지 않음 | 새 후보를 계산해 새 제안을 만들거나 운영 검토 |
| 환자 취소·거부 | 고객의 의사와 기존 상태 이력 | 상품·환불·상담 판단을 해당 기준 데이터 원본으로 전달 |
| 병원 사정 변경 | 영향을 받은 자원·시간·계획 항목과 운영 변경 | 새 제안과 고객 동의, 운영·상담 handoff |
병원 사정 변경으로 확정 예약을 바꿔야 할 때도 기존 CONFIRMED를 먼저 삭제하지 않습니다. 예약 서비스는
무엇이 언제 영향을 받았는지와 새 제안이 필요하다는 사실을 남기고, 환자에게 설명하고 동의를 받는 일은
상담·운영 흐름과 함께 수행합니다.
노쇼 페널티나 VIP 우선 예약을 이 글의 구현 기능으로 보장하지 않는 이유도 같습니다. 병원은 다음 예약 요청에 추가 확인을 요구하거나, 설명 가능한 우선순위 정책을 둘 수 있습니다. 그러나 그 정책의 기준 데이터와 판단 책임은 예약 서비스의 시간·자원 상태와 분리해야 합니다. 특히 기존 확정 예약을 자동 취소하거나 환자를 영구 블랙리스트로 만드는 정책은 현재 글의 범위가 아닙니다.
서비스별 기준 데이터와 책임 경계
섹션 제목: “서비스별 기준 데이터와 책임 경계”예약 서비스가 환자 A와 관련된 모든 사실을 소유하는 것은 아닙니다. 각 서비스가 기준 데이터 원본과 책임을 가지고, 예약 서비스는 필요한 객관적 예약 사실을 전달합니다.
| 업무 영역 | 기준 데이터 원본 | 예약 서비스에 넘기는 사실 | 예약 서비스의 책임 |
|---|---|---|---|
| 상품 관리·상품 개발 | 상품 버전(version), BOM, 소요 시간, 자원 요구, 최초 예약 규칙 | 카탈로그(catalog) 프로젝션 또는 동기화 이벤트(event) | 구매 당시 상품 기준 데이터를 검증하고 보존 |
| 구매·커머스 | 구매 계약과 원본 구매 ID | PurchaseCompleted | 구매 하나당 계획을 만들고 구매 출처 이력을 보존 |
| 예약 서비스 | AppointmentPlan, 후보·제안·보류·확정 상태, 정책 기준 정보, 이력 | 객관적 예약 이벤트(event)와 지속 보존 아웃박스(outbox) | 예약 가능 조건·예약·상태 이력을 보존 |
| 임상·시술 | 실제 시작·완료·부분 완료 | 완료·이행 사실(completion/fulfillment fact) | 계획 항목 상태와 후속 예약 영향 반영 |
| 고객 상담·CRM | 고객 프로필(profile), 상담, 민원, 보상 판단 | 예약 지연·취소·재예약 제안 같은 객관적 사실 | 상담이 판단할 수 있는 예약 사실과 인계(handoff) 제공 |
| 알림 | 연락처, 언어, 동의, 발송 이력 | 예약 이벤트(event)와 아웃박스(outbox) 처리 결과 | 예약 트랜잭션과 채널 발송을 직접 결합하지 않음 |
| 통계·외부 소비자 | 프로젝션과 지표 | 예약 이벤트(event)와 스키마 계약 | 외부 프로젝션에 원본 예약 상태를 양도하지 않음 |
이 표에서 “기준 데이터”는 단순히 데이터를 보관하는 장소가 아닙니다. 상품 관리부는 상품이 무엇인지, 커머스는 무엇을 구매했는지, 임상 서비스는 무엇을 실제로 수행했는지, CRM은 어떤 상담과 보상을 판단했는지를 각각 설명할 수 있어야 합니다. 예약 서비스는 그 사실들을 임의로 합치지 않고, 일정과 예약에 필요한 부분을 연결합니다.
현재 구현·승인 설계·운영 대기·로드맵
섹션 제목: “현재 구현·승인 설계·운영 대기·로드맵”| 사실성 표지 | 이번 글에서 말하는 범위 | 독자가 확인할 질문 |
|---|---|---|
| 현재 구현 | 버전형 상품 기준 데이터 프로젝션, 구매별 AppointmentPlan, PlannedTreatment, 희망 일정 기준 정보와 계획 상태 | 실제 원본과 테스트에서 이 계약을 확인할 수 있는가 |
| 승인된 설계 | 후보 계산 뒤 PROPOSED·HELD·CONFIRMED, 동의 기반 변경, 네 가지 예외 분기(branch) | 설계 문서를 현재 API나 운영 기능으로 과장하고 있지 않은가 |
| 운영 대기 | 브로커 전달, 알림 아웃박스(outbox) 카나리, 통계 백필과 운영 준비 상태 | 코드가 있어도 전달·복구를 실제로 검증했는가 |
| 로드맵 | 환자 포털에서 제안을 확인하고 동의하는 공개 채널, 노쇼·VIP 정책의 구체화 | 아직 정해지지 않은 정책을 현재 기능으로 약속하고 있지 않은가 |
이 상태 표지는 상품 변경 글에서 사용한 구분과도 이어집니다. 현재 AppointmentPlan 모델이 존재한다고
해서 모든 병원이 구매 직후 자동으로 특정 시간을 확정하는 것은 아닙니다. 반대로 승인된 예약 설계가
있다고 해서 현재 운영 환경의 후보 계산·알림·상담 채널이 모두 배포됐다는 뜻도 아닙니다.
다음 글에서 다룰 것
섹션 제목: “다음 글에서 다룰 것”이번 글은 이벤트 상품 하나가 만드는 첫 번째 예약을 따라가며, 상품 기준 데이터·구매 기준 데이터·
예약 계획·고객 동의의 경계를 고정했습니다. 다음 글인
N회 상품은 왜 예약 한 건이 아닌가에서는 여러
PlannedTreatment가 잔여 권리를 보존하면서 예약을 한 번씩 만드는 과정을 살펴보겠습니다.
시각 자료로 다시 보기
섹션 제목: “시각 자료로 다시 보기”아래 보조 자료는 원본 설계 문서를 대신하지 않고, 상품 기준 데이터가 상품 BOM과 예약 계획으로 전개되는 구조를 보조합니다.
근거 링크
섹션 제목: “근거 링크”- clinic-appointment 저장소
- 상품 카탈로그 정의
- AppointmentPlan 모델
- 예약 희망 기준 정보
- 예약 정책
- 구매 완료 이벤트 처리
- 상품·구매·예약 데이터 흐름
- 예약 설계
댓글
GitHub 계정으로 의견을 남기거나 reaction을 남길 수 있습니다.