Store operations
A connected physical-goods demo: stock adjustments, partial fulfillment, delivery, returns, and refund records.
Follow a complete order journey
- Inventory shows on-hand, reserved, and available units. Record a reasoned adjustment; stock cannot drop below paid orders’ reservations.
- Choose a fully paid order in Fulfillment. Select quantities, carrier, and tracking to record a partial or full shipment. Shipment quantities reduce stock and release the corresponding reservation.
- Record delivery. Only delivered units are eligible for return.
- Request a return, approve it, receive it with or without restocking, then record its refund. Receiving restocks once; refunding never restocks again. Rejected requests release return eligibility.
- Review the activity trail and preview the matching shipping, return-approval, or refund email. Messages are never sent by this demo.
State and transaction boundary
The studio store uses a separate physical-goods collection; the original Orders demo contains software licenses and is unchanged. The operations aggregate includes inventory, order-line price snapshots, captured amounts, shipments, return cases, command receipts, and an event ledger. Amounts are integer USD cents. This example excludes tax, discounts, shipping charges, exchanges, and payment processing; production pricing and accounting remain application concerns.
Reads use useOne through the active data provider. Commands use an executor. The session-memory executor serializes commands per provider/workspace, checks expected revision, applies an immutable transition, and writes the aggregate once. The same idempotency key and payload cannot apply twice; a reused key with different content fails. Failed writes keep the form draft and do not announce success. This is not a durable, multi-process transaction system.
// src/lib/commerce/adapter.ts
const demo = createDemoOperationsExecutor(provider);
// Replace the demo executor when connecting your backend:
const execute = createHttpOperationsExecutor("/api/commerce/commands", () => ({
"X-CSRF-Token": session.csrfToken,
}));
await execute({
workspaceId: "studio",
expectedRevision: workspace.revision,
commandId: crypto.randomUUID(),
command: { kind: "adjust-stock", sku: "NOTE-01", delta: 5, reason: "Delivery received" },
}, actor);
// The HTTP adapter deliberately does not serialize actor.
// Derive trusted identity, workspace access, and permissions on the server.Server contract
- Authenticate, verify CSRF where cookies are used, derive actor and tenant from the session, and authorize products.manage or orders.manage. Client demo roles are presentation controls, not authentication.
- Validate the request, lock or compare-and-swap the workspace revision, and enforce a unique workspace/idempotency key with payload fingerprint in durable storage. Cross-workspace commands must fail.
- Run the domain transition and ledger write in one transaction. Return 409 for revision or idempotency conflicts. Return the complete committed workspace as JSON.
- Integrate real carrier and payment providers server-side. Treat payment webhooks as authoritative and use the payment provider’s idempotency support. Only record a real refund after the payment operation is confirmed; a client click must not manufacture success.
- Enqueue customer emails through a transactional outbox after the event commits. Unique delivery keys prevent duplicates. Previewing or downloading a template does not send it.
Adapt the source
The framework-independent model, command adapters, email bindings, fixture, and tests are included in the starter. The full dashboard adds the operations UI and deep links using view/order query parameters. Reloading the demo resets in-memory writes. Each draft remains editable after validation, network, or conflict errors.
Edit src/lib/commerce/model.ts for transitions, adapter.ts for command transport, emails.ts for event-to-template mapping, and fixture.json for the fictional store. The fixture emitter includes this collection. The main UI is src/components/commerce/operations-workspace.tsx.