---
title: "Canonical Offer, Order ve Booking State Model"
description: "Travel sistemlerinde Offer, Order ve Booking kavramlarını ayıran canonical state modelini ve booking state machine tasarımını kurun."
slug: "canonical-offer-order-booking-state-model"
translationKey: "architecture-canonical-offer-order-booking-state-model"
locale: "tr"
type: "guide"
category: "architecture"
tags: ["offer","order","booking","state-machine","canonical-model","travel"]
publishedAt: "2026-09-27"
updatedAt: "2026-09-27"
reviewedAt: "2026-09-27"
technicalVerifiedAt: "2026-09-27"
codeExampleStatus: "illustrative"
---

Travel entegrasyonlarında en sık yapılan modelleme hatalarından biri, provider'daki tek bir `status` alanını doğrudan internal booking state yapmak. Canonical model bunun yerine **Offer, Order/Intent ve Booking'i ayrı lifecycle'lar** olarak ele almalıdır.

## Kavramsal ayrım

| Kavram | Ne ifade eder? | Mutable mı? |
|---|---|---|
| Offer | Belirli koşullarda satın alınabilir travel ürünü snapshot'ı | Yeni version üretilir |
| Order / BookingIntent | Kullanıcının seçilen offer'ı satın alma isteği | Kontrollü state transitions |
| Booking | Supplier tarafında yaratılan rezervasyon sonucu | Lifecycle boyunca değişir |

Provider terminolojisi farklı olabilir. Bazı API'ler "order", bazıları "reservation", "booking", "itinerary" veya "PNR" kullanabilir. Canonical model provider kelimesini değil davranışı normalize eder.

## Offer state

Önerilen state'ler:

```mermaid
%% title: Canonical offer state machine
%% description: Search offer'ın seçilme, reprice edilme, değişme, expire olma ve booking için kilitlenme state'leri.
stateDiagram-v2
  [*] --> Discovered
  Discovered --> Selected
  Selected --> Repricing
  Repricing --> Repriced: still valid
  Repricing --> Changed: price/policy changed
  Repricing --> Expired: unavailable
  Changed --> Selected: user accepts new offer
  Repriced --> LockedForBooking
  LockedForBooking --> Consumed: booking attempt created
```

Offer state booking state değildir. `Expired` offer, daha önce yaratılmış booking'i iptal etmez.

## Canonical Order / BookingIntent state

```text
CREATED
 -> VALIDATING
 -> READY
 -> SUBMITTING
 -> SUBMITTED
 -> COMPLETED | FAILED | UNKNOWN
```

BookingIntent kullanıcının niyetini ve idempotency sınırını temsil eder. Aynı intent için ikinci bir supplier create çağrısı ancak önceki attempt'in authoritative olarak başarısız olduğu kanıtlanırsa yapılmalıdır.

## Booking state machine

```mermaid
%% title: Canonical booking state machine
%% description: Booking'in requested, confirmed, unknown, failed, modification ve cancellation lifecycle'ı.
stateDiagram-v2
  [*] --> Requested
  Requested --> Confirmed: supplier confirmation
  Requested --> Failed: authoritative rejection
  Requested --> Unknown: ambiguous outcome
  Unknown --> Confirmed: reconciliation finds booking
  Unknown --> Failed: authoritative lookup finds none
  Confirmed --> ModificationPending: modify requested
  ModificationPending --> Confirmed: modification confirmed
  ModificationPending --> Unknown: modify outcome ambiguous
  Confirmed --> CancellationPending: cancel requested
  CancellationPending --> Cancelled: cancellation confirmed
  CancellationPending --> Confirmed: cancellation rejected
  CancellationPending --> Unknown: cancel outcome ambiguous
  Cancelled --> [*]
  Failed --> [*]
```

## UNKNOWN neden first-class state?

Timeout şu anlama gelmez:

```text
request timed out == booking failed
```

Doğru ifade:

```text
request timed out == local system outcome'u bilmiyor
```

Provider booking'i yaratmış ama response kaybolmuş olabilir. Bu yüzden UNKNOWN retry değil reconciliation tetiklemelidir.

## FAILED için kanıt seviyesi

FAILED state yalnız authoritative evidence ile verilmelidir:

- provider explicit rejection,
- valid lookup ile booking'in oluşmadığının doğrulanması,
- deterministic validation failure before provider submission.

Transport error tek başına FAILED değildir.

## Provider mapping tablosu kullanın

Provider-specific state mapping configuration veya adapter içinde explicit tutulmalıdır.

```text
provider status -> canonical state -> confidence/evidence
```

Örnek:

| Provider response | Canonical state | Evidence |
|---|---|---|
| confirmed + reference | CONFIRMED | API response |
| validation_error | FAILED | authoritative rejection |
| HTTP timeout | UNKNOWN | transport ambiguity |
| webhook cancelled | CANCELLED | async provider event |

String karşılaştırmalarını business layer'a yaymayın.

## State ve event'i ayırın

Current state hızlı okumak için materialize edilebilir; fakat evidence ayrı event log'da kalmalıdır.

```json
{
  "bookingId": "bk_42",
  "state": "CONFIRMED",
  "version": 7,
  "lastEvidence": {
    "type": "PROVIDER_CONFIRMED",
    "source": "webhook",
    "providerReference": "ABC123"
  }
}
```

Current state mutable olabilir; event history immutable olmalıdır.

## Transition guard'ları

Her transition için şu sorular cevaplanmalı:

- hangi source bu transition'ı yapabilir?
- hangi previous state'lerden izin verilir?
- aynı event tekrar gelirse ne olur?
- event eskiyse ne olur?
- provider reference yoksa transition yapılabilir mi?
- payment state bu transition için prerequisite mi?

## Modification ayrı attempt'tir

Booking modification'ı booking row'unu overwrite eden CRUD update gibi düşünmeyin.

```text
Booking
 -> ModificationAttempt
 -> provider modify
 -> CONFIRMED / FAILED / UNKNOWN
 -> reconciliation
```

Aynı model cancellation için de geçerlidir.

## Concurrency

Aynı booking'e API response, webhook ve reconciliation aynı anda gelebilir. Optimistic concurrency/version kontrolü kullanın:

```text
UPDATE booking
SET state = ?, version = version + 1
WHERE id = ? AND version = ?
```

Update başarısızsa state tekrar okunup event yeniden evaluate edilmelidir.

## Invariant'lar

Canonical modelde faydalı invariant'lar:

- CONFIRMED booking provider reference'sız olmamalı,
- CANCELLED ancak confirmed booking lifecycle'ından gelmeli,
- FAILED sonrası aynı intent otomatik yeniden submit edilmemeli; retry policy explicit olmalı,
- UNKNOWN booking terminal state değildir,
- payment CAPTURED olması booking CONFIRMED zorunluluğu yaratmaz,
- booking CONFIRMED olması payment CAPTURED zorunluluğu yaratmaz.

## Failure modes

- provider status'un canonical state'e yanlış map edilmesi,
- UNKNOWN outcome'un erken FAILED yapılması,
- stale/out-of-order event'in daha yeni state'i geri alması,
- aynı BookingIntent için paralel create attempt,
- payment state ile booking state'in birbirine karıştırılması,
- modification/cancellation sırasında version race.

## Observability

Takip edin:

- state transition count,
- invalid transition count,
- UNKNOWN count ve age,
- duplicate event count,
- out-of-order event count,
- reconciliation resolution rate,
- state başına median duration,
- modification/cancellation UNKNOWN oranı.

## Production checklist

- Offer / BookingIntent / Booking state'lerini ayrı tutun,
- terminal ve non-terminal state'leri explicit tanımlayın,
- UNKNOWN için reconciliation yolu sağlayın,
- provider mapping tablosunu adapter sınırında tutun,
- optimistic concurrency/version kontrolü kullanın,
- immutable event history saklayın,
- invalid transition ve duplicate event metriklerini izleyin.

## İlişkili model

Bu state machine event-level audit için mevcut **Booking Lifecycle Event Model** ile birlikte kullanılmalıdır. State model "şu an neredeyiz?" sorusunu; event model "buraya nasıl geldik?" sorusunu cevaplar.
