src/inventory-saga-types.tsTypeScript
@@ -0,0 +1,72 @@
1+
export type ReservationState = "pending" | "confirmed" | "expired" | "canceled";2+
3+
export interface Reservation {4+
id: string;5+
orderId: string;6+
sku: string;7+
quantity: number;8+
state: ReservationState;9+
expiresAt: Date;10+
}11+
12+
export interface ReserveInventoryCommand {13+
messageId: string;14+
orderId: string;15+
sku: string;16+
quantity: number;17+
expiresAt: Date;18+
}19+
20+
export interface OutboxEvent {21+
id: string;22+
type: "ReservationCreated" | "ReservationConfirmed" | "ReservationExpired" | "ReservationCanceled";23+
reservationId: string;24+
occurredAt: Date;25+
}26+
27+
export interface InventoryTransaction {28+
decrementStock(sku: string, quantity: number): Promise<void>;29+
decrementStockIfAvailable(sku: string, quantity: number): Promise<boolean>;30+
incrementStock(sku: string, quantity: number): Promise<void>;31+
insertReservation(reservation: Reservation): Promise<void>;32+
setReservationState(id: string, state: ReservationState): Promise<void>;33+
transitionReservation(34+
id: string,35+
from: readonly ReservationState[],36+
to: ReservationState,37+
): Promise<boolean>;38+
insertOutbox(event: OutboxEvent): Promise<void>;39+
markInboxProcessed(messageId: string): Promise<void>;40+
}41+
42+
export interface SagaDependencies {43+
database: {44+
transaction<T>(work: (transaction: InventoryTransaction) => Promise<T>): Promise<T>;45+
};46+
inventory: { available(sku: string): Promise<number> };47+
reservations: {48+
get(id: string): Promise<Reservation | undefined>;49+
listPendingExpiredAt(now: Date): Promise<readonly Reservation[]>;50+
};51+
inbox: {52+
has(messageId: string): Promise<boolean>;53+
markProcessed(messageId: string): Promise<void>;54+
};55+
outbox: { insert(event: OutboxEvent): Promise<void> };56+
newId(): string;57+
now(): Date;58+
}59+
60+
export interface EventBroker {61+
publish(input: {62+
event: OutboxEvent;63+
idempotencyKey: string;64+
}): Promise<void>;65+
}66+
67+
export interface PublishDependencies {68+
broker: EventBroker;69+
markDelivered(eventId: string): Promise<void>;70+
sleep(milliseconds: number): Promise<void>;71+
newId(): string;72+
}