B2B e-commerce architecture: start with the contract and the order process
B2B projects usually go wrong when a retail checkout is put in front of ERP data and called a customer portal. Good B2B ecommerce software architecture has to represent company accounts, negotiated terms, approvals, stock allocation and the order lifecycle as it really runs. This guide starts there.
Technical review: monkydot engineering
1. Assign sources of truth field by field
In B2B, data rarely lives in one platform. The ERP may hold the customer account and credit status, the PIM holds product enrichment, a pricing service applies the agreements and the OMS tracks fulfilment. Writing 'ERP is master' on a diagram does not settle much.
For each field, name the authority, the systems allowed to write it, the direction it flows in, how fresh it needs to be and what happens when values conflict. This stops two systems quietly owning the same data once the portal adds self-service editing.
| Domain | Usual authority | Question to answer |
|---|---|---|
| Company and billing account | ERP / CRM | Can changes made in the portal be written back, sent for approval or kept local? |
| Product enrichment | PIM | Which attributes depend on the market or the customer? |
| Contract and price inputs | ERP / pricing service | Where is the final, executable rule calculated? |
| Availability | ERP / WMS / OMS | Is the number shown stock, ATP or a delivery promise? |
| Order | Commerce, then ERP / OMS depending on the stage | Where does ownership pass over and how is that confirmed? |
2. Model companies, accounts and roles separately
A person is not the same thing as a B2B customer account. Users act on behalf of companies, branches or cost centres, and they may have different permissions in each. Model membership explicitly so that authorisation does not end up as a tag on the customer record.
Roles should describe actions: create a cart, see negotiated prices, request a quote, approve up to a limit, place an order, view invoices or manage users. Financial and commercial permissions should be enforced on the server.
- Keep an audit trail for invitations, removals and role changes.
- Decide what happens to open carts and pending approvals when someone's access changes.
- Keep SSO identity mapping separate from the mapping to commercial accounts.
- Test access across companies on every object endpoint.
| Object | What it represents | Common mistake |
|---|---|---|
| User | A person's identity | Attaching prices and credit limits directly to the user |
| Organisation | The legal or commercial account | One login shared by the whole buying team |
| Unit / branch | A delivery, budget or reporting boundary | Storing the hierarchy in free-text address fields |
| Membership | A user's role within a company or unit | Assuming each user has one global role |
| Approval policy | Who can approve what, and up to which limit | Hard-coding a single approval chain |
3. Treat contract pricing as a rules problem
A B2B price can depend on the account, product, quantity, unit, currency, date, channel, promotion eligibility and individually negotiated exceptions. A copied price list only represents that reliably if the rules really are simple.
Keep the display price separate from the price that is actually charged. Cached prices are fine for browsing, but the cart and order submission must run the authoritative rules and return an explanation that your team can inspect.
- Version agreements and record the dates they take effect.
- Store the inputs and the calculated result on the order.
- Define the order of precedence between contract, tier, promotion and manual override.
- Treat any price sent by the browser as untrusted.
| Approach | Use it when | Trade-off |
|---|---|---|
| Pre-calculated price list per account | Rules are stable and fast lookup matters | A lot of data to invalidate when anything changes |
| Runtime pricing service | Rules change often and are managed centrally | Checkout depends on that service being available |
| Hybrid | Browsing can use cached prices but checkout needs a final calculation | Two paths that must stay in parity and be reconciled |
| Quote workflow | The price needs human negotiation or project context | A longer lifecycle with an explicit expiry date |
4. Define what availability means
Physical stock, available-to-promise, available-to-sell and a delivery promise are four different values. Showing a raw warehouse quantity as if it were a sales promise leads to cancellations and extra work for customer service.
Decide whether your buyers need an exact quantity, a range, a lead time or a confirmed date. The answer may vary by account, product and warehouse. Reserve stock only where the commercial process calls for it.
| Signal | Meaning | Risk if used wrongly |
|---|---|---|
| On hand | Physical quantity recorded at a location | Ignores reservations, holds and damaged stock |
| Available to sell | Quantity after the relevant deductions | May ignore incoming supply and allocations |
| Available to promise | Quantity that can be committed under the supply rules | Needs reliable planning data |
| Delivery promise | The date or window offered for this order | Needs carrier, cut-off and fulfilment information |
5. Model the whole order lifecycle
A B2B order may start as a requisition or a quote, wait for approval on the buyer's side, go through a credit check, be split across warehouses, enter the ERP, change after confirmation and end with partial shipments, invoices and returns. A single 'placed' status cannot describe that.
Keep the status the customer sees separate from internal processing states. Record each transition with who made it, when and from which system. Avoid overwriting the commerce order with the latest ERP payload, because you lose the audit trail.
- Define the quote, requisition, approval, submission and confirmation states.
- Specify at which stages buyers can edit, cancel or reorder.
- Represent partial fulfilment, backorders, substitutions and rejections.
- Keep the purchase order number and customer references unchangeable where required.
- Link invoice, credit note, return and refund events back to the order lines.
6. Give ERP, PIM, OMS and commerce narrow jobs
There is no need to copy every ERP table into the commerce database. Build a commerce model that holds the data and identifiers needed for selling, and keep links back to the upstream records.
Use adapters to translate each system's formats into stable domain contracts. That way ERP changes stay contained and the storefront never has to know internal codes.
| System | Sensible scope | Avoid turning it into |
|---|---|---|
| ERP | Accounts, finance, commercial inputs and confirmed orders | A synchronous data source for rendering every page |
| PIM | Product taxonomy, attributes, media and the enrichment workflow | The system that decides whether an item can be sold |
| OMS / WMS | Allocation, fulfilment and operational order status | The owner of buyer identities |
| Commerce | Sessions, carts, calculated offers and taking digital orders | A second ERP |
| Search | A replaceable read model for product discovery | The source of truth for products or prices |
7. Use real-time calls only where the decision needs them
Real-time integration should follow from a business requirement. Every synchronous dependency adds latency and one more way to block the buyer. Use it for decisions that cannot work with stale data and send everything else asynchronously.
- Document the maximum acceptable delay for each field.
- Set tight timeouts on the synchronous calls you cannot avoid.
- Use queues to absorb traffic spikes and upstream maintenance windows.
- Show a pending state when a command has been accepted but not yet confirmed.
| Flow | Default | Why |
|---|---|---|
| Final price calculation | Synchronous, or executed locally | The buyer must agree to the actual terms |
| Stock reservation | Synchronous command | The confirmation needs a clear result |
| Catalogue enrichment | Asynchronous | Browsing can tolerate a controlled delay |
| Order export to ERP | Asynchronous, with confirmation | The order must survive the ERP being down |
| Fulfilment updates | Asynchronous events plus polling as a fallback | Updates matter, but they should not block a request |
| Credit check | Depends on company policy | Make it synchronous only if every order needs a current approval |
8. Design retries, idempotency and reconciliation together
Requests time out after the work has already succeeded, message brokers deliver the same message twice and users double-click. Every command with a commercial or financial effect needs a stable idempotency key and a defined response to duplicates.
Idempotency prevents the same effect from happening twice, but it does not prove that two systems agree. Reconciliation does that: it compares expected and actual state and then repairs the differences or escalates them.
- Define the scope, uniqueness and retention period of each key.
- Make consumers safe when messages arrive twice or out of order.
- Keep dead letters visible and allow them to be replayed after approval.
- Run scheduled reconciliation even when event delivery looks healthy.
| Control | Purpose | Example |
|---|---|---|
| Idempotency key | Deduplicate a retried command | One key per submitted order |
| External identifier | Match records across systems | The commerce order ID stored in the ERP |
| Inbox / processed-event log | Deduplicate consumed messages | Ignore a fulfilment event that was already handled |
| Reconciliation job | Find missing or mismatched records | Submitted orders without an ERP confirmation |
| Compensation | Reverse or offset work already done | Release a reservation after an order is rejected |
9. Decide how the system degrades before an incident happens
When a dependency goes down, nobody should have to invent a commercial policy on the spot. Decide in advance which journeys stop, which carry on with stale data and which switch to a pending state.
- Set circuit breakers and retry policies with limits.
- Protect upstream systems from retry storms.
- Give customer service a status they can share with the buyer and a clear next step.
- Test recovery and replay as well as outage detection.
| Failure | Possible degraded mode | Avoid |
|---|---|---|
| ERP unavailable | Accept the order as pending if policy allows it | Telling the buyer the ERP has accepted it before it has |
| Pricing unavailable | Use a valid cached quote or switch to a quote request | Making up a fallback price |
| Inventory unavailable | Take an enquiry or a backorder on clear terms | Confirming stock from a cache of unknown age |
| PIM feed delayed | Serve the last known catalogue and raise a freshness alert | Deleting products that are missing from one partial feed |
| Event backlog | Keep accepting within queue capacity and alert on age | Retrying without limits |
10. Secure the company boundary
B2B portals show commercially sensitive prices, orders, invoices, addresses and credit data. The main authorisation boundary is the company and its units, and the logged-in user is only part of that picture.
Use a suitable identity provider for authentication where it makes sense, but keep entitlement decisions in the commerce domain. An identity token tells you who the user is. It does not tell you which account, price list or invoice they are allowed to see.
- Check every resource against the user's active company membership.
- Require stronger controls for user management, approvals and changes to payment details.
- Audit logins, invitations, role changes, approvals, quotes and orders.
- Give service accounts scoped credentials and rotate them.
- Copy as little personal and financial data as possible into logs, analytics and search.
- Review object-level authorisation against the OWASP API risk model.
11. Monitor commercial flows and data freshness
Technical telemetry is much more useful with business context. A trace should connect the buyer's request, the company, cart, order, integration message and ERP confirmation, without writing sensitive values to the logs.
Define service-level indicators around outcomes: successful price calculations, the delay before ERP confirms an order, reservation results, how long approvals wait and how far systems drift apart.
- Pass trace and correlation IDs through messages.
- Tag telemetry by integration and lifecycle state, without personal data.
- Show freshness timestamps in the views your operations team uses.
- Attach a runbook and an owner to every alert.
| Signal | What it tells you |
|---|---|
| Time until ERP confirms an order | The health of the ERP integration and how long buyers are left uncertain |
| Price calculation errors by account | Problems in contracts or data quality |
| Reservation rejection rate | How reliable the availability promise is |
| Queue age and dead-letter count | Propagation failures, before a backlog becomes an outage |
| Reconciliation mismatch count | Systems drifting apart without anyone noticing |
| Approval cycle time | Friction in the buyer's own process, unrelated to infrastructure speed |
12. Roll out within a clear commercial boundary
Migrating every account, product and process at once is rarely a good idea. Pick a boundary that the operations team can recognise and reverse: one region, a group of accounts, a product family or an order type.
The first group should be representative enough to exercise contract prices, availability and ERP confirmation, and small enough that the team can step in manually. During the pilot, reconcile against the previous ordering channel.
- Import open quotes, carts and orders according to an agreed rule.
- Train sales, customer service, finance and warehouse teams before the buyers.
- Publish the rollback triggers and who decides on a rollback.
- Keep a manual recovery path during the pilot, then remove it deliberately.
| Phase | Scope | Done when |
|---|---|---|
| Foundation | Identity, company model, contracts and integration controls | Security and source-of-truth reviews are passed |
| Pilot | Selected accounts and products | Orders reconcile and customer service can handle the full lifecycle |
| Expand | More account groups and exceptions | Error budgets and data freshness stay within the agreed limits |
| Retire | Old ordering paths and manual workarounds | No required process depends on the old path |
13. Architecture acceptance checklist
The platform is ready when the operating model works both normally and under failure. Finished portal pages are only part of that.
- Every critical field has one authority, a freshness rule and a reconciliation path.
- Users, companies, units, memberships and approvals are modelled as separate concepts.
- Contract price precedence, effective dates and audit evidence are defined.
- The availability terms used internally match the promise shown to buyers.
- The order lifecycle covers confirmation, partial fulfilment and rejection.
- ERP, PIM, OMS, commerce and search have narrow, documented responsibilities.
- Synchronous dependencies are limited to decisions that need current data.
- Commercial commands are idempotent and identifiers are kept across systems.
- Each failure mode has defined behaviour for customers and for your team.
- Company-level authorisation is tested on every private resource.
- Telemetry covers business transitions, queue age, freshness and drift.
- The pilot has defined account groups, rollback triggers and trained staff.
Sources and further reading
- Medusa documentation: Pricing Module ↗
- Medusa documentation: Inventory Module ↗
- Medusa documentation: Product Module ↗
- Medusa documentation: Workflows ↗
- Medusa documentation: Events and subscribers ↗
- NIST: Digital Identity Guidelines ↗
- OWASP API Security Top 10 ↗
- Stripe API: Idempotent requests ↗
- OpenTelemetry: Observability primer ↗
- Microsoft Azure Architecture Center: Compensating Transaction pattern ↗
- IETF RFC 9110: HTTP Semantics ↗