You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
This is mean to be a realistic implementation of a shopping cart and order management system written in typescript and targeted to Next.js. The backend and infrastructure run in docker locally and will eventually run on Vercel and AWS.
Long Description
Temporal Commerce Demo — Project Description
A full-stack e-commerce application built entirely on Temporal durable execution. Every state transition — from adding an item to a cart through order fulfillment and delivery — is a Temporal workflow. No message queues, no cron jobs, no saga orchestrators. The business logic is the infrastructure.
Every e-commerce system is a distributed state machine. A shopping cart lives in one service, payment processing in another, inventory in a third, and fulfillment in a fourth. The traditional approach wires these together with REST calls, message queues, cron jobs, and reconciliation scripts.
This project demonstrates that Temporal eliminates that entire infrastructure layer. The application has:
No message queue — no Kafka, no RabbitMQ, no SQS. Workflow signals replace all async messaging.
No cron jobs — the inventory service workflow replaces "run every 5 minutes" with condition(() => dirty, '5m').
No dead-letter queues — Temporal's retry policies and activity timeouts handle all transient failures.
No saga orchestrator — the checkout workflow is the saga. Steps, compensations, and timeouts are just workflow code.
graph TB
subgraph browser["Browser"]
ui["React Storefront + Admin Panel"]
end
subgraph nextjs["Next.js Server"]
actions["Server Actions<br/>(cart-actions.ts, admin-order-actions.ts)"]
api["API Routes<br/>(/api/search, /api/product, /api/dev)"]
end
subgraph temporal["Temporal Server (6 Task Queues)"]
cart["Cart Workflow<br/>cart-queue"]
checkout["Checkout Workflow<br/>checkout-queue"]
oms["Order Workflow<br/>oms-queue"]
fulfillment["Fulfillment Workflow<br/>fulfillment-queue"]
inventory["Inventory Service<br/>inventory-queue"]
identity["Identity Workflows<br/>identity-queue"]
end
ui -->|"fetch / form submit"| actions
ui -->|"GET"| api
actions -->|"gRPC: updateWithStart, query, signal"| temporal
temporal -->|"Activities"| cassandra[("Cassandra<br/>Write Store")]
temporal -->|"Activities"| elasticsearch[("Elasticsearch<br/>Read Projections")]
api -->|"Search / Read"| elasticsearch
Loading
The Next.js server actions layer is the sole bridge between the browser and the Temporal cluster. Every cart mutation is a Temporal workflow update. Every product query hits Elasticsearch read projections that are kept in sync by workflow activities.
Workflow Domains
Cart — Durable Entity Pattern
The cart is a long-running Temporal workflow that acts as a live, queryable entity. There are no database reads for cart state — the workflow is the cart.
Pattern
Implementation
Lazy creation
updateWithStart atomically creates-or-updates the cart workflow on the first "Add to Cart" click
Live state
React UI reads cart state via Temporal queries; mutations are Temporal updates with synchronous return values
Infinite lifetime
continueAsNew after 100 updates resets the event history while preserving full cart state
Graceful shutdown
await condition(allHandlersFinished) ensures in-flight update handlers complete before continueAsNew
Child orchestration
Checkout is started as a child workflow with ABANDON parent close policy
Checkout — Multi-Step State Machine
Checkout orchestrates the shipping → payment → review → processing → complete lifecycle. Each step is advanced by a Temporal update with guard validation.
Pattern
Implementation
Step guards
Each update validates the current step before proceeding — shipping can be set from shipping, payment, or review (enabling back-navigation)
Reservation management
Inventory reservations are renewed at checkout start, released on timeout/cancellation, confirmed on success
Timeout
condition(() => complete, '1 hour') auto-cancels stale checkouts and releases inventory
Cross-workflow signaling
Checkout signals the parent cart workflow with the result via getExternalWorkflowHandle
Activity-driven spawning
On order submission, an activity starts the OMS workflow — fully decoupling checkout from order management
Order Management (OMS) — Lifecycle Orchestration
The OMS workflow manages an order from placement through delivery. It coordinates supplier assignments, tracks fulfillment status, and maintains audit history.
Pattern
Implementation
Supplier routing
resolveSupplierAssignments activity decides which supplier handles each line item
Decoupled fulfillment
Fulfillment is started via an activity (not startChild), making it a standalone workflow with its own lifecycle
Signal-driven updates
Fulfillment status flows upward via signals — the OMS aggregates across all supplier orders to derive order-level status
Status projections
Every status change is indexed to Elasticsearch for real-time admin panel updates
Audit trail
Every status transition is recorded in the order_status_history Cassandra table
Fulfillment — Strategy-Based Execution
The fulfillment workflow receives pre-decided supplier orders and executes the appropriate fulfillment strategy for each.
Elasticsearch serves as the read projection layer with full-text search and faceted filtering:
Index
Projected By
Consumer
products
Reindex API (bulk)
Storefront search, product detail
collections
Reindex API (bulk)
Collection navigation
orders
OMS workflow activities
Admin order list
supplier_orders
OMS workflow activities
Admin order detail
CQRS Flow
sequenceDiagram
participant UI as Browser
participant SA as Server Action
participant WF as Temporal Workflow
participant C as Cassandra
participant ES as Elasticsearch
UI->>SA: Add to Cart
SA->>WF: updateWithStart (gRPC)
WF->>WF: Update state in memory
WF->>C: Persist via Activity
WF->>ES: Project via Activity
WF-->>SA: Return updated cart
SA-->>UI: Render new state
Loading
Unified Worker Architecture
All six domain workers run in a single Node.js process, sharing one gRPC connection to Temporal. Each domain has its own task queue, workflow registrations, and activity implementations.
Task queue isolation means a slow fulfillment activity cannot block cart operations. Each domain processes work independently even though they share a connection. In production, these can be split into separate deployments for independent scaling.
Key Temporal Patterns Demonstrated
#
Pattern
Where Used
1
updateWithStart — atomic lazy entity creation
Cart
2
Query/Update handlers — workflow as live entity
Cart, Checkout, OMS
3
continueAsNew — infinite entity lifetime
Cart, Inventory Service
4
Parent-child with ABANDON policy
Cart → Checkout
5
Step-based state machine with update guards
Checkout
6
condition() with timeout — reservation TTL
Checkout, Inventory
7
Cross-workflow signaling via getExternalWorkflowHandle
The application follows a principle of Redemptive State Recovery: when a workflow operation fails, the system returns to the last known good state instead of crashing.
Payment failure → checkout returns to the payment step with an error message
Checkout timeout → reservations released, cart returns to active
Terminal workflow → server action wrapper catches WorkflowNotFoundError and returns null for graceful UI degradation
Worker crash → Temporal automatically replays the workflow from the last checkpoint; no state is lost
Project link
https://github.com/night-heron-software/temporal-commerce-demo
Language
TypeScript
Short description (max 256 chars)
This is mean to be a realistic implementation of a shopping cart and order management system written in typescript and targeted to Next.js. The backend and infrastructure run in docker locally and will eventually run on Vercel and AWS.
Long Description
Temporal Commerce Demo — Project Description
A full-stack e-commerce application built entirely on Temporal durable execution. Every state transition — from adding an item to a cart through order fulfillment and delivery — is a Temporal workflow. No message queues, no cron jobs, no saga orchestrators. The business logic is the infrastructure.
Stack: Next.js 15 · Temporal TypeScript SDK · Apache Cassandra · Elasticsearch
Scale: 111 source files · ~15,400 LOC · 6 Temporal workflow domains · 2,689 products
Why This Exists
Every e-commerce system is a distributed state machine. A shopping cart lives in one service, payment processing in another, inventory in a third, and fulfillment in a fourth. The traditional approach wires these together with REST calls, message queues, cron jobs, and reconciliation scripts.
This project demonstrates that Temporal eliminates that entire infrastructure layer. The application has:
condition(() => dirty, '5m').updateWithStartgives atomic create-or-update.allHandlersFinishedgives graceful shutdown.Architecture
graph TB subgraph browser["Browser"] ui["React Storefront + Admin Panel"] end subgraph nextjs["Next.js Server"] actions["Server Actions<br/>(cart-actions.ts, admin-order-actions.ts)"] api["API Routes<br/>(/api/search, /api/product, /api/dev)"] end subgraph temporal["Temporal Server (6 Task Queues)"] cart["Cart Workflow<br/>cart-queue"] checkout["Checkout Workflow<br/>checkout-queue"] oms["Order Workflow<br/>oms-queue"] fulfillment["Fulfillment Workflow<br/>fulfillment-queue"] inventory["Inventory Service<br/>inventory-queue"] identity["Identity Workflows<br/>identity-queue"] end ui -->|"fetch / form submit"| actions ui -->|"GET"| api actions -->|"gRPC: updateWithStart, query, signal"| temporal temporal -->|"Activities"| cassandra[("Cassandra<br/>Write Store")] temporal -->|"Activities"| elasticsearch[("Elasticsearch<br/>Read Projections")] api -->|"Search / Read"| elasticsearchThe Next.js server actions layer is the sole bridge between the browser and the Temporal cluster. Every cart mutation is a Temporal workflow update. Every product query hits Elasticsearch read projections that are kept in sync by workflow activities.
Workflow Domains
Cart — Durable Entity Pattern
The cart is a long-running Temporal workflow that acts as a live, queryable entity. There are no database reads for cart state — the workflow is the cart.
updateWithStartatomically creates-or-updates the cart workflow on the first "Add to Cart" clickcontinueAsNewafter 100 updates resets the event history while preserving full cart stateawait condition(allHandlersFinished)ensures in-flight update handlers complete beforecontinueAsNewABANDONparent close policyCheckout — Multi-Step State Machine
Checkout orchestrates the shipping → payment → review → processing → complete lifecycle. Each step is advanced by a Temporal update with guard validation.
shipping,payment, orreview(enabling back-navigation)condition(() => complete, '1 hour')auto-cancels stale checkouts and releases inventorygetExternalWorkflowHandleOrder Management (OMS) — Lifecycle Orchestration
The OMS workflow manages an order from placement through delivery. It coordinates supplier assignments, tracks fulfillment status, and maintains audit history.
resolveSupplierAssignmentsactivity decides which supplier handles each line itemstartChild), making it a standalone workflow with its own lifecycleorder_status_historyCassandra tableFulfillment — Strategy-Based Execution
The fulfillment workflow receives pre-decided supplier orders and executes the appropriate fulfillment strategy for each.
wf.sleep()timers simulate processing (15s) → shipping (15s) → delivery (15s)MANUAL_FULFILLMENT=truepauses at each stage, waiting for Temporal signals to advancesupplierType— simulated, Printify dynamic, or customInventory — CQRS Event Processor
The inventory service is a single long-running workflow that replaces an entire message queue consumer + cron job infrastructure.
condition(() => dirtySkus.size > 0, '5m')gives both event-driven and time-driven behaviorsignalWithStartcreates the inventory service on the first inventory mutationIdentity — User and Store Management
The identity domain manages user accounts, shoppers, API tokens, and store configuration through Temporal workflows.
Data Architecture
Write Side — Cassandra
Cassandra serves as the durable write store with partition-key isolation:
products,variants,collectionsorders,orders_by_customer,orders_by_confirmationorder_status_historyinventory_stock,inventory_reservations_wusers,shoppers,api_tokensRead Side — Elasticsearch
Elasticsearch serves as the read projection layer with full-text search and faceted filtering:
productscollectionsorderssupplier_ordersCQRS Flow
sequenceDiagram participant UI as Browser participant SA as Server Action participant WF as Temporal Workflow participant C as Cassandra participant ES as Elasticsearch UI->>SA: Add to Cart SA->>WF: updateWithStart (gRPC) WF->>WF: Update state in memory WF->>C: Persist via Activity WF->>ES: Project via Activity WF-->>SA: Return updated cart SA-->>UI: Render new stateUnified Worker Architecture
All six domain workers run in a single Node.js process, sharing one gRPC connection to Temporal. Each domain has its own task queue, workflow registrations, and activity implementations.
block-beta columns 3 block:worker["Unified Worker Process (worker.ts)"]:3 columns 3 cart["Cart Worker\ncart-queue"] checkout["Checkout Worker\ncheckout-queue"] oms["OMS Worker\noms-queue"] fulfillment["Fulfillment Worker\nfulfillment-queue"] inventory["Inventory Worker\ninventory-queue"] identity["Identity Worker\nidentity-queue"] end space:3 conn["Shared NativeConnection (single gRPC)"]:3 cart --> conn checkout --> conn oms --> conn fulfillment --> conn inventory --> conn identity --> connTask queue isolation means a slow fulfillment activity cannot block cart operations. Each domain processes work independently even though they share a connection. In production, these can be split into separate deployments for independent scaling.
Key Temporal Patterns Demonstrated
updateWithStart— atomic lazy entity creationcontinueAsNew— infinite entity lifetimeABANDONpolicycondition()with timeout — reservation TTLgetExternalWorkflowHandlestartChild)Error Handling — Redemptive State Recovery
The application follows a principle of Redemptive State Recovery: when a workflow operation fails, the system returns to the last known good state instead of crashing.
activeWorkflowNotFoundErrorand returnsnullfor graceful UI degradationProject Structure
Quick Start
Technology Stack
Related Documentation
Author(s)
Jeff Romine/Night Heron Software