튜토리얼: Fascia로 예약 시스템 만들기
이 튜토리얼에서는 Fascia를 사용하여 완전한 예약 시스템을 단계별로 구축합니다. Entity(엔티티) 정의부터 결제 처리 Tool(도구) 설계, 리스크 분석, Spec Registry 등록까지 전체 과정을 다룹니다.
만들게 될 것
이 튜토리얼을 완료하면 다음을 갖추게 됩니다:
- Reservation Entity -- 6개 상태를 가진 상태 머신 (
pending->confirmed->checked_in->completed)과 비즈니스 Invariant(불변식)를 포함합니다. - processPayment Tool -- 결제 캡처, 트랜잭션 관리, 보상 플로우(자동 환불)를 포함하는 결제 처리 도구입니다.
- 리스크 분석 결과 -- Risk Engine이 Tool의 안전성을 어떻게 검증하는지 확인합니다.
사전 요구 사항
- Fascia 플랫폼 접근 권한 (얼리 액세스 또는 로컬 개발 환경)
- Entity 개념 이해 -- 필드, 관계, 상태 머신, Invariant
- Tool 개념 이해 -- Flow(플로우) 그래프, 트리거, Execution Contract
Step 1: Reservation Entity 정의
예약 시스템의 핵심은 Reservation Entity입니다. 고객이 특정 시간대와 자원에 대해 생성하는 예약을 나타냅니다.
Entity Spec 전체
{
"name": "Reservation",
"version": 1,
"description": "A booking made by a customer for a specific time slot and resource",
"fields": {
"customerId": { "type": "reference", "referenceTo": "Customer", "required": true },
"resourceId": { "type": "reference", "referenceTo": "Resource", "required": true },
"startDate": { "type": "datetime", "required": true },
"endDate": { "type": "datetime", "required": true },
"totalPrice": { "type": "number", "required": true },
"depositAmount": { "type": "number", "required": false, "default": 0 },
"notes": { "type": "string", "required": false },
"cancellationReason": { "type": "string", "required": false }
},
"relationships": {
"customer": { "type": "belongsTo", "target": "Customer", "foreignKey": "customerId" },
"resource": { "type": "belongsTo", "target": "Resource", "foreignKey": "resourceId" },
"payments": { "type": "hasMany", "target": "Payment" }
},
"statusMachine": {
"states": ["pending", "confirmed", "checked_in", "completed", "cancelled", "no_show"],
"initialState": "pending",
"transitions": [
{ "from": "pending", "to": "confirmed" },
{ "from": "pending", "to": "cancelled" },
{ "from": "confirmed", "to": "checked_in" },
{ "from": "confirmed", "to": "cancelled", "guard": "now() < reservation.startDate" },
{ "from": "confirmed", "to": "no_show", "guard": "now() > addHours(reservation.startDate, 1)" },
{ "from": "checked_in", "to": "completed" }
]
},
"invariants": [
{ "name": "endDateAfterStart", "expression": "reservation.endDate > reservation.startDate", "message": "End date must be after start date" },
{ "name": "totalPricePositive", "expression": "reservation.totalPrice > 0", "message": "Total price must be positive" },
{ "name": "depositNotExceedTotal", "expression": "reservation.depositAmount <= reservation.totalPrice", "message": "Deposit cannot exceed total price" }
],
"rowLevelAccess": true,
"ownerField": "customerId"
}
상태 머신 상세
Reservation Entity는 6개의 상태를 가집니다:
| 상태 | 의미 |
|---|---|
pending | 예약이 생성되었으나 아직 결제되지 않은 상태입니다 |
confirmed | 결제가 완료되어 예약이 확정된 상태입니다 |
checked_in | 고객이 실제로 도착하여 체크인한 상태입니다 |
completed | 서비스 이용이 완료된 상태입니다 |
cancelled | 예약이 취소된 상태입니다 |
no_show | 고객이 예약 시간에 나타나지 않은 상태입니다 |
상태 전이에는 Guard(가드) 표현식이 포함될 수 있습니다. Guard는 Value DSL로 작성된 조건식으로, 전이가 허용되는 조건을 정의합니다:
confirmed->cancelled:now() < reservation.startDate-- 시작 시간 이전에만 취소가 가능합니다.confirmed->no_show:now() > addHours(reservation.startDate, 1)-- 시작 시간으로부터 1시간이 경과한 후에만 노쇼로 처리할 수 있습니다.