Composable commerce and OMS: how to design fulfilment, split shipments and ERP/WMS integrations
A composable commerce OMS is the decision layer between checkout, inventory, ERP, WMS, 3PL, stores and customer service. Its job is not just shipping a parcel; it decides where each order line is fulfilled from, when stock is reserved, when to split shipments, and how to handle holds, returns, cancellations and reconciliation. At GMI, we design that model after DDT, usually on MedusaJS, NestJS, PostgreSQL and event-driven queues, with client-owned code.
OMS is not a store add-on. It is the operating system for an order
An Order Management System in commerce should not be understood as a screen with an order list. In mature ecommerce, OMS is a decision layer: it receives the order from checkout, checks the delivery promise, reserves stock, selects the fulfilment source, splits shipments, talks to WMS/3PL, updates statuses and manages exceptions.
The difference between a simple order admin and a real OMS appears when the business sells from multiple warehouses, stores, marketplaces, countries or B2B/B2C channels. One order can touch ERP, WMS, PIM, payment provider, carrier, customer service and accounting. Without one ownership model, operations fall back to spreadsheets.
What is a composable OMS?
A composable OMS is order management designed as a set of modules and integrations, not as an inseparable part of one commerce monolith. It can use MedusaJS as the commerce core, NestJS for custom business logic, PostgreSQL for operational state, queues for asynchronous integrations and APIs to ERP, WMS, 3PL, POS and carriers.
MACH does not mean every process must become a microservice. It means system boundaries are intentional: the storefront does not contain warehouse logic, checkout does not wait for every external system, and OMS does not mix order status with accounting. Good composability reduces coupling where business change is highest.
Flow map: checkout → OMS → fulfilment
The simplest way to think about OMS: checkout creates a customer commitment, OMS turns it into an operational plan, and WMS/3PL performs the physical work. If these three worlds are mixed, every promotion, delivery or warehouse change can regress checkout.
The map should include at least: order capture, payment authorization, inventory reservation, sourcing decision, fulfilment order, pick/pack/ship, carrier label, shipment event, customer notification, invoice/reconciliation and return. Every step needs a status, owner, retry policy and reversal rule if something fails.
- Checkout: price, payment and delivery promise.
- OMS: decision, reservation, split, status and exceptions.
- WMS/3PL: picking, packing, label, shipment and confirmations.
- ERP/Finance: invoice, correction, accounting availability and reconciliation.
When you need OMS as a separate layer
Not every store needs a dedicated OMS. One warehouse, one country, standard delivery, low return volume and no B2B can usually live inside the commerce platform or ERP. The problem starts when fulfilment logic becomes a business variable, not a simple consequence of order placement.
The signals are concrete: multiple stock locations, ship-from-store, click and collect, marketplace, 3PL, backorders, preorder, bundles/kits, bulky products, partial shipments, different SLA per customer segment, B2B approval flow, credit limits, returns to a different location than shipment and customer service that must change fulfilment source without asking developers.
Inventory reservation: the most common failure point
Medusa Inventory Module supports inventory across locations, reservation management and availability checks. That is a strong foundation, but the project still has to answer the business question: when does stock stop being a catalogue promise and become a reservation for a specific order?
In practice, you need separate states for available, reserved, allocated, picked, shipped, returned and damaged. A reservation should have owner, reason, expiration, location, relationship to order or cart and release rules. Without this, peak season ends in overselling or the opposite: healthy stock frozen by abandoned carts and unfinished payments.
B2B adds another dimension: stock reservation can depend on approval flow, credit limit, payment terms or minimum order value. The OMS must know whether the order is ready for fulfilment or only for the next approval step.
Order routing and split shipments
Shopify FulfillmentOrder shows an important pattern: after order creation, a routing process decides which locations are responsible for fulfilment, and one order can have more than one fulfilment order. This is a useful mental model beyond Shopify: the customer order and the operational work in the warehouse are not always one object.
In a composable OMS, sourcing should consider more than availability. Shipping cost, cut-off time, warehouse capacity, customer SLA, dimensions, temperature, country, carrier restrictions, return risk, margin, promotions and business priorities all matter. The cheapest warehouse is not always best if it delays a VIP customer or creates a three-package split shipment.
Split shipment is a tool, not a goal. You need explicit rules for when to split an order, wait for consolidation, offer a substitute, cancel a line or let customer service decide manually. Without those rules, OMS turns operational complexity into customer-communication complexity.
MedusaJS, NestJS and an event-driven backbone
Medusa Fulfillment Module provides fulfilment management, provider integrations, restrictions by location and rules, and different fulfilment forms such as shipping and pickup. Medusa Workflows let teams build flows from steps with rollback mechanisms. That helps when operations must stay consistent across many integrations.
In practical GMI architecture, MedusaJS is usually not the whole OMS by itself. We treat it as the commerce core and modular foundation, while custom sourcing, SLA, B2B, reconciliation or ERP/WMS integration rules often live in NestJS services and queues. That keeps checkout from waiting on a 3PL response, while critical integrations get retry, idempotency and dead-letter handling.
Event-driven does not mean "everything async". Payment capture, stock reservation and order creation have different consistency needs than email, carrier label generation or dashboard updates. A good design says which decisions are synchronous, which are eventually consistent and where manual recovery is required.
ERP, WMS and 3PL: system of record versus system of action
The biggest OMS problems do not come from missing APIs, but from unclear ownership of facts. ERP can be the financial system of record for invoices and receivables, WMS the warehouse work system, PIM the product-data source and OMS the operational decision system. If every system tries to be everything, statuses drift after the first return.
For every integration, write the contract: who creates the record, who may change it, who sends the event, what the idempotency key is, how retry works, which statuses are final, what happens on partial failure and who has the dashboard to unblock cases manually. This is not documentation theatre. It is what later saves customer service and finance.
In many companies, ERP should receive the organized result of operations, not participate in every checkout click. OMS can maintain the operational fulfilment state and sync with ERP at control points: order accepted, invoice ready, shipment confirmed, return received, credit note issued.
Returns, cancellations and exceptions are part of OMS
If OMS handles only the happy path, it is not production-ready. Partial return, cancellation after 3PL handoff, missing SKU, damaged item, refused delivery, chargeback, invoice correction, product substitution and customer-service override all need statuses and financial rules.
The return flow should know whether goods go back to sellable stock, quality control, disposal or supplier. Refund should not be only a payment-provider action; it must connect to order, invoice, shipment, inventory and customer communication. B2B adds credit notes, approval and limits.
Exceptions reveal whether composable architecture works. If every correction needs a developer, the architecture is only technically modular. Operationally, it is still a monolith living in a few people's heads.
How to measure whether OMS improves fulfilment
OMS should have business, operational and technical metrics. "Orders processed" alone says little. Better metrics are order cycle time, time to release to warehouse, split shipment rate, fulfilment cost per order, pick failure rate, oversell rate, stock reservation age, return cycle time, manual intervention rate and reconciliation backlog.
The engineering team also needs system signals: queue depth, event lag, webhook failure rate, retry count, dead-letter volume, idempotency conflicts, latency per integration and number of orders stuck in a status. Without these metrics, OMS can look good in a demo and still be unmanageable in December.
The best implementation decision starts with a baseline. Measure current order handling cost and time, manual corrections, customer-service response time and logistics error cost. Only then can you decide whether composable OMS should start with inventory, routing rules, WMS integration or returns.
How GMI designs composable OMS after DDT
At GMI, we do not start by choosing an OMS tool. We start with DDT: order lifecycle, stock locations, systems of truth, SLA, exceptions, returns, B2B approval, ERP/WMS/3PL, customer service and metrics. Only then do we decide what belongs in MedusaJS, what belongs in custom NestJS backend, and what should stay in external WMS or ERP.
A typical stack for composable commerce OMS is a Next.js storefront, MedusaJS commerce core, NestJS for order orchestration and integrations, PostgreSQL for operational state, Redis/SQS/RabbitMQ for queues, observability for events and dashboards, and sometimes React Native/Expo for warehouse, store or courier apps.
After DDT, fixed price can be discussed responsibly because scope is no longer "build an OMS". Scope describes concrete flows: order capture, reservations, sourcing, split shipments, WMS handoff, returns, reconciliation, monitoring and handover. Code, documentation and context stay on the client side, without vendor lock-in.
Checklist before implementing OMS
Before deciding on OMS, bring operations, ecommerce, finance, customer service and IT around one order map. If every function has its own version of statuses, align the language before writing code. Otherwise the new system only distributes old misunderstandings faster.
- List all stock locations and define which are sellable, fulfilment, return and service locations.
- Define statuses for order, fulfilment order, shipment, invoice, refund and return.
- Decide stock reservation timing and release rules for cart, payment, cancellation and approval flow.
- Write sourcing rules: cost, SLA, cut-off, capacity, dimensions, country, carrier and customer priority.
- Design retry, idempotency and manual recovery for every ERP/WMS/3PL/carrier integration.
- Agree metrics: split rate, fulfilment cost, oversell, stuck orders, return cycle time and manual intervention.
Sources and further reading
Sources used in this update: https://docs.medusajs.com/resources/commerce-modules/fulfillment (Medusa Fulfillment Module), https://docs.medusajs.com/resources/commerce-modules/inventory (Medusa Inventory Module), https://docs.medusajs.com/resources/commerce-modules/stock-location (Medusa Stock Location Module), https://shopify.dev/docs/api/admin-rest/latest/resources/fulfillmentorder (Shopify FulfillmentOrder lifecycle).
Related GMI guide: MedusaJS B2B storefronts.
For async architecture, see event-driven commerce.
For real-time inventory, see quick commerce realtime inventory.
For the broader architecture decision, see MACH stakeholder guide.
Frequently asked questions
- What is a composable OMS?
- A composable OMS is an order management layer built from modules and integrations, not a closed part of a monolith. It connects checkout, inventory, ERP, WMS, 3PL, carriers and customer service through APIs and events so fulfilment decisions can change without rewriting the whole platform.
- When does a store need a separate OMS?
- A separate OMS makes sense when fulfilment is not simple: multiple warehouses or stores, 3PL, ship-from-store, click and collect, marketplace, backorders, bundles, B2B approval flow, partial shipments or returns to different locations. In that case, the commerce platform order admin is usually not enough.
- How does OMS decide where to fulfil an order from?
- OMS should analyze availability, shipping cost, SLA, cut-off, warehouse capacity, dimensions, country, carrier restrictions, margin, return risk and customer priority. The nearest or cheapest warehouse is not always best if it delays delivery or creates an unnecessary split shipment.
- How does MedusaJS help with OMS and fulfilment?
- MedusaJS provides fulfilment, inventory and stock-location modules plus workflows with steps and rollback. It is a strong commerce-core foundation. In more complex projects, custom sourcing, SLA, B2B, ERP/WMS and reconciliation rules often belong in NestJS and event-driven queues.
- How should a company prepare for a composable OMS implementation?
- Start with an order lifecycle map: statuses for order, fulfilment order, shipment, invoice, refund and return. Then document stock locations, reservation timing, sourcing rules, ERP/WMS/3PL integrations, retry, idempotency, manual recovery and success metrics.
- Can GMI implement OMS under fixed price?
- After DDT and scope alignment, GMI can propose fixed price for a concrete OMS scope: reservations, routing, split shipments, WMS handoff, returns, reconciliation, monitoring and handover. Fixed price is responsible only when processes, integrations and edge cases are known.
Content updated: July 11, 2026