The storefront is often the easy part. You connect the commerce API. Products appear. The CMS delivers landing-page content. Add-to-cart works. Everything looks clean. Then someone asks a much harder question:
*Where should the product description live?
*
The commerce backend already has one. The CMS has another because the content team needs more control. The ERP owns the SKU. The WMS knows the actual stock.
Now you don't have a frontend problem.
You have an ownership problem. This is one of the first decisions I would make when designing a headless ecommerce stack with Medusa and a CMS such as Sanity or Strapi:
**
For every important piece of data, decide which system is allowed to be authoritative.
**
Everything else should consume, reference, or synchronize that data deliberately.
Start with ownership, not APIs
A typical headless stack might look simple:
Next.js Storefront
│
┌──────────┴──────────┐
│ │
Commerce API Content API
│ │
Medusa Sanity / Strapi
│
┌───────┼────────┐
│ │ │
ERP WMS Payments
Connecting these systems isn't usually the difficult architectural decision. The difficult part is deciding what each system controls. Consider a product page for a running shoe.
The page may need:
- SKU
- title
- price
- available sizes
- inventory
- marketing description
- product images
- SEO title
- buying guide
- related campaign content
That looks like one "product" to the customer. Technically, it doesn't have to be one record owned by one system.
A product is really several kinds of data
*This distinction is useful:
*
There is no universal table that works for every business. A retailer with an ERP and PIM will draw these boundaries differently from a smaller DTC store.
The important part is not which system appears in the right-hand column. The important part is that there is a right-hand column.
Don't make two systems authoritative for the same field
*Imagine this:
*
Medusa
Product title:
"Classic Running Shoe"
↕ sync
CMS
Product title:
"Everyday Running Shoe"
Which title is correct?
You can write synchronization logic, but that doesn't answer the question.
What happens if both records change?
What if the CMS webhook fails?
What if an older event arrives after a newer one?
What happens during a retry?
A cleaner model is to define one owner and let the other system reference what it needs.
*For example:
*
CMS
Summer Running Campaign
├── Heading
├── Hero Image
├── Editorial Copy
└── Featured Product
│
└── medusa_product_id
│
▼
Medusa
│
Product / Variants
Price / Availability
The CMS decides which product to feature. The commerce system decides what that product currently costs and whether it can be purchased. Those are different responsibilities.
*Keep transactional data close to commerce
*
Medusa separates commerce capabilities into areas such as products, pricing, inventory, carts, customers, orders, payments and fulfillment. That makes it useful to think of Medusa as more than a place where product records happen to live.
For a typical implementation, I would be cautious about moving data such as live pricing, cart state, order state or inventory availability into the CMS simply because editors can access it there.
Consider inventory.
A CMS might display:
_inventory = 12
_
But while that content is cached, two orders are placed and the WMS receives another reservation.
The number is no longer 12.
Inventory is operational state. Editorial content isn't. They should not automatically use the same ownership or caching strategy.
For teams extending commerce logic, integrations, workflows, or storefront connections, this is also where deeper Medusa development decisions start to matter: the boundary around Medusa should be designed before more systems begin depending on it.
Let the CMS own the content it is good at
Now consider a merchandising team preparing a seasonal collection.
They may need:
- campaign headline
- hero media
- editorial description
- product story
- size guide
- FAQ
- buying guide
- related articles
- SEO copy
That is a very different workflow from calculating price or reserving inventory.
This is where a structured CMS makes sense.
With Sanity
Sanity stores content as structured documents and supports references between content.
That makes a pattern like this possible:
Campaign
├── headline
├── description
├── hero
├── featuredProducts[]
│ └── commerceProductId
└── buyingGuide
The editorial structure stays in the CMS while the commerce identifier connects it to the operational product.
That separation becomes particularly useful when working through content models, references and storefront integration during Sanity development.
The key is still the same:
> Reference commerce data where possible instead of creating a second authoritative copy of it.
With Strapi
The same architectural principle can be implemented with Strapi using content types and relations.
For example, the CMS might model:
Landing Page
│
├── Hero
├── Promo Banner
├── Product Story
└── Featured Product Reference
The exact implementation is different, but the ownership question isn't.
When planning Strapi development for an ecommerce stack, I would decide first whether Strapi is storing editorial representations of products or becoming an actual source of product data.
Those are two very different architectures.
Not all stale data has the same consequence
This is where headless ecommerce gets more interesting. Suppose the storefront serves data that is 60 seconds old.
For this:
Campaign heading:
"Summer Running Collection"
a short delay may be acceptable.
For this:
Price:
$89 → $109
the consequence is different.
And for this:
Inventory:
1 remaining → 0 remaining
the consequence may be different again.
So instead of asking:
*"How long should we cache our API responses?"
*
Ask:
*"How stale can this specific type of data safely become?"
*
That leads to much better caching decisions. Content, price, inventory and checkout state don't need identical consistency models just because they appear on the same page.
Design for one system being unavailable
There is another question worth asking before production:
*What happens when one part of the stack goes down?
*
If the CMS becomes temporarily unavailable, should checkout stop working?
Usually, that would be an unnecessary dependency. If previously published editorial content is cached, the storefront may still be able to serve it while commerce remains operational.
CMS unavailable
│
├── Cached published content → available
└── New publishing → unavailable
Medusa available
│
├── Product data → available
├── Cart → available
├── Checkout → available
└── Orders → available
Now reverse the situation.
If the commerce backend is unavailable, cached editorial pages may still render, but live pricing, cart actions and checkout can be affected.
This is why drawing system boundaries is useful beyond clean code.
*The boundary also defines the failure domain.
*
A practical ownership matrix
Before implementing the integrations, I like reducing the architecture to something this simple:
The asterisks are intentional. In a simple store, Medusa may own those records directly.
In a larger operation, the ERP, PIM or WMS may be the real source of truth and Medusa may receive or expose the data needed for commerce. The architecture should follow the operating model, not the other way around.
A few mistakes worth avoiding
The problems I've seen in headless architectures usually aren't caused by having too many APIs. They're caused by unclear responsibility.
A few patterns deserve extra scrutiny:
**Duplicating complete product records in the CMS.
**Store only what the editorial workflow genuinely needs, or maintain an explicit synchronization strategy.
*Letting multiple systems update the same field.
*
Synchronization does not replace ownership.
Treating inventory like content.
Operational state usually needs different freshness guarantees.
Making checkout depend on the CMS.
Editorial availability and transaction availability don't need to share a failure domain.
Connecting records by names.
Names change. Stable IDs are much safer boundaries between systems.
Using webhooks without planning for retries.
Assume events can fail, repeat, or arrive later than expected.
The architecture gets easier once ownership is clear
Headless ecommerce gives us freedom to choose specialized systems. That freedom also removes some of the boundaries a traditional platform chooses for us.
Medusa can handle commerce. Sanity or Strapi can handle structured editorial content. An ERP, PIM or WMS may control another part of the product lifecycle.
The storefront can bring all of it together. But bringing data together doesn't mean every system should own it. Before adding another integration, I would ask one question:
*If two systems disagree about this value, which one wins?
*
If the team can't answer that clearly, the integration probably isn't fully designed yet.
That's usually the harder part of headless ecommerce.
Not the frontend.


Top comments (0)