Documentation Index
Fetch the complete documentation index at: https://api-docs.vpms.io/llms.txt
Use this file to discover all available pages before exploring further.
ADR-0003: Rate Domain 마이그레이션 런북
본 문서는 본 브랜치 머지 후 운영 환경에서 수행해야 할 절차를 정리한다.1. Phase 별 산출물 요약
| Phase | 산출물 |
|---|---|
| Phase 1 | Prisma 스키마 재설계. legacy ari-* 4개 도메인 제거. 신규 rate-plan / inclusion 도메인 + AuditLog는 audit-svc로 위임. |
| Phase 2 | pricing-rules.service / rate-resolver.service / rate-audit.publisher / rematerialization.service. |
| Phase 3a | folio / reservation / bill / block 의 legacy ari 참조 정리 (rename or stub). |
| Phase 3b | GraphQL rate-plan.gql + resolver. Registry 등록. |
| Phase 4 | Redis hot cache + horizon-extension.scheduler + rate-archive.scheduler + restriction.service. |
| Tests | pricing-rules.service.test + restriction.service.test. |
| Docs | docs/api-reference/core-svc/rate-plan.mdx + 본 런북. |
2. 머지 전 확인
pnpm prisma generate재실행 — generated client 가 새 모델/필드 반영하는지 확인.pnpm test— node:test 가 신규 unit test 를 인식하는지 확인.pnpm nx run core-svc:build— TS 컴파일 통과 확인.pnpm nx run core-svc:lint— biome 위반 없는지.
3. DB 마이그레이션
운영 데이터가 없는 시점이므로prisma migrate reset 후 신규 스키마로 마이그레이션 적용.
- legacy 테이블 DROP:
ariRates,ariRateAdjustments,ariRateAdjustmentMappings,ariRatePlans,ariRateHistories,ariRatePlanHistories,ariRatePlanChannelAdjustments,ariRatePlanInclusions,ariInclusions,ariInclusionHistories,ariPeriods,ariPeriodRanges,ariPackages,ariPackageHistories,ariFolioRateSnapshot,ariFolioInclusionSnapshot. - 신규 테이블 CREATE:
ratePlans,dailyRates,ratePlanRestrictions,ratePlanOccupancyPrices,ratePlanRoomTypes,ratePlanChannelMappings,ratePlanInclusions,ratePlanHistories,rateSeasons,rematerializationProposals,folioRateSnapshots,folioInclusionSnapshots,inclusions,inclusionHistories. - 변경:
prices.ariPackageId→prices.ratePlanId.
4. BullMQ cron 등록
worker bootstrap 시 다음 작업을 등록한다.5. Phase 5 booking integration 진행 상태
Phase 3a 에서 NotImplemented stub 으로 비워둔 booking 흐름을 신규 RatePlan / DailyRate 도메인 기반으로 재구현하는 작업. 본 브랜치에서 다음까지 완료되었다:✅ 완료
snapshot-creation.service:prepareSnapshotData/commitPreparedSnapshots/createSnapshots가RateResolverService.resolve로 가격을 해석하고FolioRateSnapshot/FolioInclusionSnapshot을 생성한다.ratePlanSnapshot/inclusionSnapshotJSON 으로 immutability 를 보장.folio-amount-calculator.service: 신규 snapshot 의rateAmount/inclusionAmount/totalAmount를 그대로 활용. history join 불필요.bill-creation.service:FolioRateSnapshot단위로 Bill 발행.BillBriefKey.CHARGE_RESERVATION_DAILY/CHARGE_INCLUSION적용.FolioRatePlanInput / RatePlanInput / RatePlanUpdateInput입력 shape 을 신규ratePlanId(ID) + roomTypeId(ID) + occupancy + amountOverride로 마이그레이션.reservation-modify.helpers.deriveRatePlanInclusionInputs:RatePlanInclusion직접 조회로 default inclusion 목록 구성.reservation.service.createReservationForFolio: 예약 확정 전RestrictionService.assertBookable호출로 CTA/CTD/StopSell/LOS 위반 차단.block.service: FolioRatePlanInput 호출부 신규 shape 으로 마이그레이션.
✅ schema 마이그레이션 (추가)
ReservationNight.ratePlanId:Int→Bytes(ULID).rateId:Int→BigInt?(DailyRate.id 참조, sparse 허용).BlockComposition.ratePlanId/rateId: 동일 패턴으로Bytes? / BigInt?.reservation-night.helpers.createReservationNights시그니처를string / bigint?로 갱신, 호출 흐름 복원.ReservationNight.ratePlan/rate및BlockComposition.ratePlan/rateresolver 가 신규 RatePlan / DailyRate row 를 직접 findUnique 한다.- GraphQL
ReservationNight.ratePlanId/rateId/BlockComposition.ratePlanId/rateId타입을ID!/String으로 정정 (BigInt 직렬화).
✅ snapshot JSON shape 정리 (추가)
BlockCompositionRateSnapshotshape:ratePlanId→string(ULID),rateId→string|null(BigInt 직렬화).currencySymbol폐기.ratePlanVersion / rateIdVersion은 deprecated 표시 후 legacy archive 호환용으로만 유지 (신규 row 는 안 채우는 것이 권장).- block.gql 의
BlockCompositionRateSnapshottype /BlockCompositionRateSnapshotInput도 동일하게 갱신.
✅ BigInt id 직렬화 (추가)
DailyRate 와 RatePlanRestriction 의 PK 가BigInt 인데 GraphQL ID! 는 string 이라
resolver 경계에서 직렬화 필요. serializeBigIntId / serializeBigIntIds 헬퍼를
rate-plan.resolver 에 추가하고 setDailyRate / getRatePlanRestrictions /
setRatePlanRestriction / block.resolver.rate / reservation.resolver.rate /
folio.resolver.appliedRate 에 적용. 모든 BigInt 반환 경로가 string 으로 일관 직렬화.
⏭ Out of scope (별도 PR)
- Bill domain 의
Payment.currencySymbol컬럼 — ADR 원래 범위 외. 통화 코드만 저장하는 표준에 맞춰 별도 마이그레이션 PR 권장. - 신규
appliedPackageresolver 가 현재 RatePlan 을 반환 — package 개념이 inclusion bundle 로 흡수된 만큼 GraphQL surface 에서 제거할지 별도 논의. - E2E 통합 테스트 (예약 → folio snapshot → bill 발행 happy path).
6. PostgreSQL partitioning
ADR-0001 §2.6 정책에 따라daily_rates 테이블을 stay_date 월별로 파티셔닝한다.
초기 row 수가 적은 동안은 단일 테이블로 운영 가능. 다음 조건 중 하나 충족 시 도입:
- row 수 1천만 초과
- 시설 1천 개 초과
- 캘린더 쿼리 P95 > 200ms
pg_partman 자동 파티션 생성 + monthly cron 으로 13개월 cutoff partition 을
archive 테이블로 DETACH → COPY → DROP 한다.
7. 롤백 정책
운영 데이터 마이그레이션이 시작된 후에는 롤백이 어렵다. 본 변경은 운영 데이터가 없는 시점에 적용되므로 다음 안전망만 유지:- 머지 전 main 브랜치 백업 tag 생성 (
pre-rate-redesign). - 머지 후 24시간 핫스탠바이 운영자 배치.
- 외부 통합(채널 매니저 등)은 별도 ramp-up 일정으로 분리.