設計書 → 40.apis → reservations → create_reservation → detail-design_gen.md
create_reservation detail design
- Operation:
createReservation
- Endpoint:
POST /reservations
1. 正常系入力
| Location |
Name |
Type |
説明 |
body |
request |
CreateReservationRequest |
- |
header |
Idempotency-Key |
IdempotencyKey |
- |
Request body CreateReservationRequest
| 項目 |
型 |
説明 |
resourceId |
ResourceId |
予約する資源を一意に識別するIDです。 |
startAt |
BookingTimestamp |
予約開始日時です。15分刻みで、現在より後かつ30日以内です。 |
endAt |
BookingTimestamp |
予約終了日時です。15分刻みで、開始と同じ日本時間の日付内です。 |
purpose |
PurposeText |
予約目的です。1〜200文字です。 |
2. 正常系前提
- 条件分岐: 有効期限内の成功記録がIdempotency-Keyに紐づいている場合。: 不成立
- 条件分岐: 成功記録の正規化入力と再送された要求が一致しない場合。: 不成立
- 条件分岐: 予約対象の資源が有効でない場合。: 不成立
- 条件分岐: 取消済みを除き、半開区間が重なる確定予約がある場合。: 不成立
- 例外処理: ROUTER_HANDLED_EXCEPTIONS: 発生しない
3. 正常系リソース変更
DB resources 更新
- SQL:
002_update_resources_control_version.sql
- 目的: 同一資源の予約作成・取消・無効化を競合させるため、資源の内部制御版を進めて現在値を取得する。
| カラム |
カラム説明 |
パラメータ/SQL式 |
値の取得元 |
control_version |
同一資源の予約作成・取消・無効化を競合させる内部制御版。 |
control_version + 1 |
SQL式: control_version + 1 |
DB idempotency_records 削除
- SQL:
004_delete_idempotency_records.sql
- 目的: 有効期限を過ぎた成功記録を新規要求として扱うため削除する。
| カラム |
カラム説明 |
パラメータ/SQL式 |
値の取得元 |
principal_id |
要求した利用者の認証subject。 |
principal_id |
認証主体: caller.principalId |
idempotency_key |
Idempotency-Keyヘッダの値。 |
idempotency_key |
record.idempotency_key |
DB users 作成
- SQL:
005_insert_users.sql
- 目的: 初めて予約する利用者を記録する。既存利用者は変更しない。
| カラム |
カラム説明 |
パラメータ/SQL式 |
値の取得元 |
principal_id |
認証基盤のsubject。 |
principal_id |
認証主体: caller.principalId |
first_seen_at |
初めて予約した日時。 |
first_seen_at |
clock.now() |
DB reservations 作成
- SQL:
006_insert_reservations.sql
- 目的: 予約を確定状態の初期版で保存する。
| カラム |
カラム説明 |
パラメータ/SQL式 |
値の取得元 |
reservation_id |
予約ID。 |
reservation_id |
自動生成: uuid4() を str 化 |
resource_id |
予約対象の資源ID。 |
resource_id |
request.body.resourceId |
owner_principal_id |
予約者の認証subject。 |
owner_principal_id |
認証主体: caller.principalId |
start_at |
予約開始日時。UTCで保存する。 |
start_at |
request.body.startAt |
end_at |
予約終了日時。UTCで保存し、この時刻を含まない。 |
end_at |
request.body.endAt |
purpose |
予約目的。1〜200文字。本人と管理者だけに返す。 |
purpose |
request.body.purpose |
status |
予約状態。confirmedまたはcancelled。 |
'confirmed' |
SQL式: 'confirmed' |
row_version |
楽観ロック用の行バージョン。 |
1 |
SQL式: 1 |
DB reservation_events 作成
- SQL:
007_insert_reservation_events.sql
- 目的: 予約作成の履歴を予約と同じtransactionで追記する。
| カラム |
カラム説明 |
パラメータ/SQL式 |
値の取得元 |
event_id |
履歴ID。 |
event_id |
自動生成: uuid4() を str 化 |
reservation_id |
対象の予約ID。 |
reservation_id |
reservation.reservation_id |
actor_principal_id |
操作した利用者の認証subject。 |
actor_principal_id |
認証主体: caller.principalId |
action |
操作種別。createdまたはcancelled。 |
'created' |
SQL式: 'created' |
occurred_at |
操作日時。 |
occurred_at |
clock.now() |
DB idempotency_records 作成
- SQL:
008_insert_idempotency_records.sql
- 目的: 元の成功応答を予約と同じtransactionで24時間の成功記録として保存する。
| カラム |
カラム説明 |
パラメータ/SQL式 |
値の取得元 |
principal_id |
要求した利用者の認証subject。 |
principal_id |
reservation.owner_principal_id |
idempotency_key |
Idempotency-Keyヘッダの値。 |
idempotency_key |
header.Idempotency-Key |
request_hash |
正規化した要求本文のSHA-256。 |
request_hash |
入力/生成値から _request_hash(...) で算出 |
response_payload |
元の成功応答のJSON。 |
response_payload |
reservation.model_dump_json(by_alias=True) |
expires_at |
成功記録の有効期限。 |
expires_at |
clock.now() + IDEMPOTENCY_TTL |
4. 正常系レスポンス
- Response model:
CreateReservationResponse
| 項目 |
説明 |
値の取得元 |
reservationId |
予約を一意に識別するIDです。 |
reservation.reservation_id |
resourceId |
予約対象の資源を一意に識別するIDです。 |
reservation.resource_id |
ownerPrincipalId |
予約者を表す認証主体IDです。 |
reservation.owner_principal_id |
startAt |
予約開始日時です。 |
reservation.start_at |
endAt |
予約終了日時です。この時刻を含みません。 |
reservation.end_at |
purpose |
予約目的です。本人と管理者だけに返します。 |
reservation.purpose |
status |
予約の状態です。 |
reservation.status |
version |
予約取消の楽観ロックに使う版です。 |
reservation.version |