# TANRAIL ONE — Core Sales Event Architecture

## 1. Domain Events

The Core Sales engine will dispatch explicit domain events rather than heavily coupling logic across modules. 

### `OrderConfirmed`
- **Fired When**: An Order transitions from `DRAFT` to `CONFIRMED`.
- **Primary Listener**: `DeductInventoryListener`
- **Action**: Iterates through physical `OrderLine` records and calls `InventoryService::issue()`.

### `OrderCancelled`
- **Fired When**: An Order transitions to `CANCELLED`.
- **Primary Listener**: `RestoreInventoryListener`
- **Action**: Iterates through physical `OrderLine` records and calls `InventoryService::receive()`.

### `PaymentReceived`
- **Fired When**: A Payment is successfully logged against an Order.
- **Primary Listener**: `CheckOrderCompletionListener`
- **Action**: Checks if Total Payments >= Order Total. If so, updates Order state.

## 2. Idempotency & Duplicate Prevention
Event listeners must be idempotent.
- `DeductInventoryListener` will check the Order state. If the Order is already `CONFIRMED` and stock has been deducted, it gracefully aborts.
- To prevent race conditions, the listener operates within the primary Database Transaction of the `SalesService`. If `InventoryService` throws a negative stock exception, it bubbles up and aborts the entire `OrderConfirmed` action.

## 3. Concurrency
Because the inventory deduction is handled synchronously inside the `SalesService` transaction boundary (via the Event Listener), the existing `InventoryService::issue()` pessimistic lock (`lockForUpdate()`) natively protects against simultaneous order confirmations trying to consume the same remaining stock.
