Orchestrating Five Systems Without Middleware

May 20, 2024


The integration challenge: Connect Medusa (commerce), Odoo (ERP), a custom Product Configurator, Strapi (PIM), and a Data Management Portal. Keep data consistent. Avoid middleware hell.

Traditional approach: Build complex middleware. Handle sync failures. Debug data inconsistencies. Maintain brittle connections.

Our approach: Event-driven architecture using Medusa's workflow engine. No middleware. No data duplication where it doesn't belong.

System Responsibilities

Medusa:

Odoo (ERP):

Product Configurator:

Data Management Portal:

Strapi (PIM):

Each system owns its domain. Medusa coordinates without trying to own everything.

The Integration Pattern

Two-way sync with Odoo:

Orders flow bidirectionally. Web orders go from Medusa → Odoo. Sales rep orders go Odoo → Medusa. This ensures consistency regardless of order source.

External IDs for linking:

Every entity (customer, order, product) has both a Medusa ID and an Odoo ID. We store the mapping, so systems stay loosely coupled.

// Simplified example of how we link entities
interface Customer {
  id: string;              // Medusa ID
  externalId: string;      // Odoo ID
  organizationId: string;  // Links multiple buyers
}

Caching for performance:

Customer shipping data caches in Medusa, updated only when Odoo sends change webhooks. This makes checkout fast—no waiting on ERP queries.

Workflows for data flow:

Medusa workflows handle order placement:

  1. Validate cart against configurator pricing
  2. Calculate shipping from Odoo data
  3. Create order in Medusa
  4. Send webhook to Odoo
  5. Listen for fulfillment updates

Built-in rollback if any step fails. No orphaned data.

Custom Pricing Integration

This was the complex part. Pricing depends on:

How it works:

  1. Customer configures product in storefront
  2. Configurator calculates base price using DMP rules
  3. DMP applies org-specific discounts
  4. Price validation happens at checkout
  5. Final price stores in Medusa order
  6. Order data syncs to Odoo with price locked

No price stored until validated. No chance of stale pricing at checkout.

Organization Accounts

Extended Medusa's customer model:

// Simplified schema
customer {
  id: string;
  email: string;
  organizationId: string;  // New field
  externalId: string;      // Links to Odoo
}

This unlocks:

One field. Massive impact on B2B experience.

What Worked

Event-driven beats request-response. Webhooks mean systems don't wait on each other. Order placement doesn't block on Odoo. Fulfillment updates arrive asynchronously.

Cache what changes slowly. Shipping rules don't change every second. Cache them in Medusa, update only when Odoo signals changes.

Validate at boundaries. Pricing calculations happen in the configurator, but Medusa validates before order creation. Catch inconsistencies early.

External IDs for loose coupling. Systems reference each other through IDs, not tight integration. Replacing one system doesn't cascade everywhere.

What I Learned

Middleware is often a crutch. If you need complex middleware, your architecture probably has unclear responsibilities.

Workflows are underrated. Having structured patterns for multi-step operations with rollbacks prevents entire classes of bugs.

Caching strategy matters as much as architecture. Know what changes frequently (cart data) vs rarely (shipping rules). Cache accordingly.

ERP integration doesn't mean ERP owns everything. Commerce platforms are better at commerce. Let them handle it, sync what the ERP needs.

Read about this integration pattern in Medusa's ERP guide.


Technical implementation by the rb2 team. I worked on the architecture and integration patterns.