Supply Chain Context¶
The Supply Chain context manages vendor relationships and bulk ordering for the restaurant chain. It coordinates with the Inventory context to ensure adequate stock across locations.
Purpose¶
A 500-location restaurant chain can't have each location ordering independently — bulk purchasing saves money and ensures consistency. The Supply Chain context handles the procurement lifecycle from order creation through approval, shipping, receipt, and issue reporting.
Interview Connection¶
From the Head Chef's interview:
"I source local ingredients required to prepare our menu items."
The Head Chef works at the corporate level to standardize ingredients and manage supplier relationships. The Supply Chain context formalizes this process with tracked purchase orders.
Types¶
type PurchaseOrderId is Id(SupplyChain.PurchaseOrder) with {
briefly "Purchase order identifier"
described by "Unique identifier for a purchase order."
}
type VendorId is UUID with {
briefly "Vendor identifier"
described by "Unique identifier for a vendor."
}
type PurchaseOrderStatus is any of {
PoDraft,
PoSubmitted,
PoApproved,
PoShipped,
PoReceived,
PoDisputed
} with {
briefly "Purchase order status"
described by "Current status of a purchase order."
}
type OrderLineItem is {
lineItemName is String(1, 200)
lineItemQuantity is Decimal(10, 2)
lineItemUnit is String(1, 20)
lineItemUnitPrice is Decimal(10, 2)
} with {
briefly "Order line item"
described by "A single item in a purchase order."
}
Note the PoDisputed status — if a shipment has quality
issues, the purchase order can be disputed rather than simply
accepted.
Entity: PurchaseOrder¶
The PurchaseOrder entity has a 5-command lifecycle:
entity PurchaseOrder is {
command CreateBulkOrder is {
purchaseOrderId is PurchaseOrderId
vendorId is VendorId
vendorName is String(1, 200)
orderLineItems is many OrderLineItem
requestedDeliveryDate is Date
}
command ApproveOrder is {
purchaseOrderId is PurchaseOrderId
approvedAt is TimeStamp
}
command ShipOrder is {
purchaseOrderId is PurchaseOrderId
shippedAt is TimeStamp
trackingNumber is optional String(1, 100)
}
command ReceiveShipment is {
purchaseOrderId is PurchaseOrderId
receivedAt is TimeStamp
receivedCondition is String(1, 200)
}
command ReportIssue is {
purchaseOrderId is PurchaseOrderId
purchaseOrderIssueType is String(1, 50)
purchaseOrderIssueDescription is String(1, 1000)
issueReportedAt is TimeStamp
}
// Events: BulkOrderCreated, OrderApproved, OrderShipped,
// ShipmentReceived, IssueReported
state ActivePurchaseOrder of PurchaseOrder.PurchaseOrderStateData
handler PurchaseOrderHandler is {
on command CreateBulkOrder {
morph entity SupplyChain.PurchaseOrder to state
SupplyChain.PurchaseOrder.ActivePurchaseOrder
with command CreateBulkOrder
tell event BulkOrderCreated to
entity SupplyChain.PurchaseOrder
}
on command ApproveOrder {
tell event OrderApproved to
entity SupplyChain.PurchaseOrder
}
on command ShipOrder {
tell event OrderShipped to
entity SupplyChain.PurchaseOrder
}
on command ReceiveShipment {
tell event ShipmentReceived to
entity SupplyChain.PurchaseOrder
}
on command ReportIssue {
tell event IssueReported to
entity SupplyChain.PurchaseOrder
}
}
}
The lifecycle: Create → Approve → Ship → Receive → (optional) Report Issue.
The ReportIssue command can trigger a follow-up that
changes the order status to PoDisputed, initiating a
resolution process with the vendor.
Repository¶
repository PurchaseOrderRepository is {
schema PurchaseOrderData is relational
of purchaseOrders as PurchaseOrder
index on field PurchaseOrder.purchaseOrderId
index on field PurchaseOrder.vendorId
index on field PurchaseOrder.purchaseOrderStatus
}
The index on vendorId supports vendor-centric views — how
many orders are outstanding with a specific vendor, what's the
order history, etc.
Design Decisions¶
Why no adaptor to Inventory? When a shipment is received
at a restaurant location, the Inventory context's
ReceiveStock command is called directly by the receiving
clerk. The supply chain tracks the corporate-level purchase
order, while inventory tracks the location-level stock. These
are different levels of abstraction that don't need tight
coupling.
Why many OrderLineItem? Bulk purchase orders typically
contain multiple items — different cuts of meat, sauces,
packaging, etc. The many keyword models a collection of line
items within a single order.