Orders Domain
The orders domain provides order lifecycle management for TypeScript backends. It handles order placement, state machine transitions (pending, confirmed, processing, shipped, delivered, canceled, refunded), address management, and price totals.
Install
Section titled “Install”npx @backcap/cli add ordersDomain Model
Section titled “Domain Model”Order Entity
Section titled “Order Entity”The Order entity is the aggregate root. All state transitions go through Order methods — confirm(), process(), ship(), deliver(), cancel().
import { Order } from "./domains/orders/domain/entities/order.entity";import { OrderItem } from "./domains/orders/domain/entities/order-item.entity";import { Address } from "./domains/orders/domain/value-objects/address.vo";
const address = Address.create({ street: "123 Main St", city: "Paris", country: "France", postalCode: "75001",}).unwrap();
const item = OrderItem.create({ id: crypto.randomUUID(), productId: "prod-123", quantity: 2, unitPriceCents: 1999,}).unwrap();
const result = Order.create({ id: crypto.randomUUID(), items: [item], shippingAddress: address, billingAddress: address,});
if (result.isOk()) { const order = result.unwrap(); console.log(order.status.value); // "pending" console.log(order.totalCents); // 3998 console.log(order.itemCount); // 1}| Field | Type | Description |
|---|---|---|
id | string | Unique identifier (UUID) |
items | readonly OrderItem[] | Order line items |
status | OrderStatus | Current order status |
shippingAddress | Address | Shipping address |
billingAddress | Address | Billing address |
totalCents | number | Computed sum of all line totals |
itemCount | number | Number of items |
State Machine
Section titled “State Machine”Orders follow a strict state machine enforced by a VALID_TRANSITIONS map:
- pending → confirmed via
order.confirm() - pending → canceled via
order.cancel() - confirmed → processing via
order.process() - confirmed → canceled via
order.cancel() - processing → shipped via
order.ship() - processing → canceled via
order.cancel() - shipped → delivered via
order.deliver() - delivered → refunded (future)
Cancellation is only allowed from pending, confirmed, or processing — not from shipped or delivered.
OrderItem Entity
Section titled “OrderItem Entity”const item = OrderItem.create({ id: crypto.randomUUID(), productId: "prod-123", quantity: 2, unitPriceCents: 1999,});
if (item.isOk()) { console.log(item.unwrap().lineTotal); // 3998}Value Objects
Section titled “Value Objects”| VO | Description |
|---|---|
OrderStatus | Enum with 7 states and valid transitions map |
Address | Street, city, country, postal code (all required, trimmed) |
Use Cases
Section titled “Use Cases”| Use Case | Description |
|---|---|
PlaceOrder | Create a new order with items and addresses |
ConfirmOrder | Transition order to confirmed status |
ShipOrder | Transition order to shipped status (must be processing) |
CancelOrder | Cancel order (only from pending/confirmed/processing) |
GetOrder | Get order with all details |
ListOrders | List all orders |
Contract
Section titled “Contract”import { createOrderService } from "./domains/orders/contracts";import type { IOrderService, OrderOutput } from "./domains/orders/contracts";
const orders: IOrderService = createOrderService({ orderRepository,});
// Place an orderconst result = await orders.placeOrder({ items: [{ productId: "prod-123", quantity: 2, unitPriceCents: 1999 }], shippingAddress: { street: "123 Main", city: "Paris", country: "FR", postalCode: "75001" }, billingAddress: { street: "123 Main", city: "Paris", country: "FR", postalCode: "75001" },});
// Lifecycle transitionsawait orders.confirmOrder("order-id");await orders.shipOrder("order-id");await orders.cancelOrder("order-id");
// Queryconst order = await orders.getOrder("order-id");const all = await orders.listOrders();| Port | Description |
|---|---|
IOrderRepository | Order persistence (findById, findAll, save, update) |