All Posts
Headless Commerce June 30, 2026

Migrating from Shopify Plus to Medusa, Vendure, or Saleor: Plugin Architecture, Data Migration Order, and Cutover Patterns (June 2026)

Migrating off Shopify Plus to an open source headless backend is not a single project. It is a sequence of nested cutovers, each with its own failure modes and its own rollback story. The plugin architecture of the target platform decides how much of your custom Shopify Plus logic ports over cleanly, the data migration order decides whether the cutover window is hours or weeks, and the dual write window decides whether you can roll back if something goes wrong. The hard parts are not on the brochures.

Migrating off Shopify Plus to an open source headless backend is not a single project. It is a sequence of nested cutovers, each with its own failure modes and its own rollback story. The brochures from Medusa, Vendure, and Saleor describe the technical capabilities of the target platforms. The brochures do not describe the order in which a real migration has to happen, the dual write windows that keep order volume safe during the switch, or the plugin architecture choices that determine how much of your custom Shopify Plus logic ports over cleanly. Those are the parts that decide whether the migration ships in six months or eighteen.

We have run platform migrations from Shopify Plus to all three open source backends across several brand profiles, and the cutover patterns are converging on a shared shape with platform specific variations. This is a field guide to the migration order, the plugin architecture differences that matter for porting custom logic, the data migration sequencing that keeps the cutover window short, and the dual write pattern that buys a rollback option. None of this is hypothetical; all of it has shipped.

Quick Comparison: What Ports, What Rewrites

Migration concern Medusa Vendure Saleor
Custom Shopify Functions (cart, discount, delivery) Rewrite as Medusa modules Rewrite as Vendure plugins Rewrite as Saleor apps
Shopify Scripts (deprecated, but still in many stacks) Rewrite as Medusa workflows Rewrite as Vendure event handlers Rewrite as Saleor sync webhooks
Shopify Flow automations Rewrite as Medusa subscribers Rewrite as Vendure plugins Saleor's new Sequences feature ports cleanly
Liquid theme templates Discard, build new storefront Discard, build new storefront Discard, build new storefront
Hydrogen/Remix storefront Re-point to Medusa API, partial rewrite Re-point to Vendure GraphQL, larger rewrite Re-point to Saleor GraphQL, minimal rewrite
Custom checkout (Shopify Plus Checkout Extensibility) Build custom checkout on Medusa Build custom checkout on Vendure Saleor Checkout SDK ports closest
Stripe integration First party Medusa module First party Vendure plugin First party Saleor app
NetSuite integration Build via Medusa workflow + webhook Build via Vendure plugin + scheduled job Build via Saleor webhook + async job
Multi region / multi currency Native (Sales Channels) Native (Channels) Native (Channels, strongest of the three)
B2B price lists / catalogs Available, less mature Strongest of the three Available, MOFU maturity
Admin UI customization React components on Medusa Admin Angular components on Vendure Admin React components on Saleor Dashboard
Migration tooling availability (2026) Open source CLI (medusa-import-shopify) Community plugin (vendure-shopify-importer) First party Saleor migration suite

The single biggest porting cost across all three migrations is custom Shopify Functions and Shopify Flow logic. Brands that have invested heavily in Shopify Functions (cart transform, discount, delivery customization, payment customization, fulfillment constraints) pay the largest rewrite tax because the JavaScript / Rust Functions API does not map cleanly to any of the three platforms' extension models. Brands that have stayed close to the Shopify Plus default behavior pay the smallest rewrite tax and can complete the platform migration in a quarter.

The Plugin Architecture That Decides the Rewrite Cost

Each platform has a different extension model, and the model decides how custom Shopify logic ports.

Medusa's module architecture is the most flexible of the three. A custom module is a TypeScript class that registers with the Medusa container, exposes services and routes, and integrates with the workflow engine for orchestration. Custom Shopify Function logic (cart transform, discount, delivery) ports to Medusa modules that subscribe to cart and order events and mutate the cart through the workflow engine. The rewrite is mechanical for simple Functions and creative for complex Functions that depend on Shopify specific cart constructs.

// Medusa module example: cart transform equivalent for a Shopify discount Function
import { Module, IEventBusModuleService } from "@medusajs/framework/modules"

class B2BVolumeDiscountModule extends Module {
  static MODULE_NAME = "b2b_volume_discount"

  async subscribe(eventBus: IEventBusModuleService) {
    eventBus.subscribe("cart.updated", async ({ id }) => {
      const cart = await this.cartService.retrieve(id, { relations: ["items", "customer.groups"] })
      if (!cart.customer?.groups.some(g => g.name === "b2b")) return

      for (const item of cart.items) {
        const tier = this.tierFor(item.quantity)
        if (tier) {
          await this.cartService.applyAdjustment(cart.id, item.id, {
            amount: item.unit_price * tier.discount_pct * item.quantity,
            description: `B2B tier ${tier.name} discount`,
          })
        }
      }
    })
  }
}

Vendure's plugin architecture is the most opinionated. Plugins extend the NestJS application with services, resolvers, entities, and event handlers, all typed against the Vendure schema. The opinionation is the strength: a Vendure plugin written correctly is type safe against the entire commerce surface, and the migration from Shopify Function to Vendure plugin produces code that the team can reason about. The cost is a steeper learning curve for engineers coming from Shopify Plus's loose extension model.

Saleor's app architecture is the most loosely coupled. Saleor apps run as separate services (typically Next.js applications) that communicate with Saleor over GraphQL and webhooks. The app architecture matches the way Shopify Plus apps work, which makes the migration mental model the closest to the Shopify Plus baseline. The cost is operational: a brand running 8 Saleor apps runs 8 separate deployments, with the matching observability and on call surface.

The Migration Order That Actually Works

The order matters more than any other decision in the project. The wrong order forces dual writes for months past the necessary window and adds a 30 percent to 50 percent surcharge to the project timeline. The right order minimizes the dual write window to days, not months.

The sequence that ships:

  1. Read replicas first. Stand up the target platform with read access to a copy of the Shopify Plus data. Products, variants, collections, customers, and price lists land in the target platform as a one way sync. No checkout, no orders, no inventory writes. This phase typically runs four to six weeks and produces a working catalog on the new platform with no business impact if it breaks.

  2. Storefront first, then admin. Build the new storefront against the target platform's read APIs and ship it to a fraction of traffic (typically a region or a subdomain). Keep checkout pointing to Shopify Plus during this phase by either rendering the Shopify checkout from the new storefront or redirecting cart submissions to a Shopify checkout URL. The admin UI on the brand side stays on Shopify until the checkout cuts over, because the merchandising team is the highest leverage user and they should not switch tools until the new system is proven.

  3. Inventory dual write. Before checkout cuts over, set up bidirectional inventory sync between Shopify Plus and the target platform. This is the highest risk phase of the project because inventory inconsistency between the two systems is the failure mode that bites worst at cutover. We run inventory dual write for at least two weeks before checkout cutover, with hourly reconciliation reports and a tight tolerance threshold (zero inventory units of drift) that triggers a human investigation on every variance.

  4. Checkout cutover. Switch the checkout from Shopify Plus to the target platform on the same fraction of traffic the new storefront serves. This is the only step that cannot be undone cheaply: orders placed on the target platform after this step are not in Shopify Plus and rolling back requires a reverse data migration that nobody wants to run. The cutover window is typically a four hour deployment with a hot rollback plan that re-points the checkout URL back to Shopify Plus if the conversion rate falls outside the tolerance band in the first hour.

  5. Order management cutover. Move order management (refunds, returns, exchanges, customer service tools) from the Shopify Plus admin to the target platform's admin. This is the merchandising team's switching day, and it should happen at least two weeks after checkout cutover so that the team has muscle memory in the new tool before they have to use it under refund pressure.

  6. NetSuite/ERP integration cutover. Move the ERP integration from Shopify Plus to the target platform last. This is the longest pole on most migrations because ERP integrations are the slowest moving systems on the brand side and the most tightly coupled to Shopify's specific data model. Plan a four to six week window where both Shopify Plus and the target platform write into NetSuite, with deduplication on the NetSuite side, until the team is confident that the target platform integration produces the same downstream behavior as the Shopify Plus integration.

  7. Shopify Plus decommission. Wind down the Shopify Plus subscription only after a full month of business operation on the target platform with zero Shopify Plus writes. The decommission is the cheapest step in the project but the one teams rush; an extra month of Shopify Plus subscription cost is small insurance against discovering a missing integration four weeks after the data is gone.

The Dual Write Window and the Reconciliation Story

The dual write window between Shopify Plus and the target platform is the operational core of the migration. Every write to either system has to propagate to the other within the reconciliation interval (typically 5 minutes for inventory, 15 minutes for orders, hourly for customer profile changes). The propagation runs through a message bus (we default to AWS SQS for both platforms, with one queue per entity type) and each message carries a source system identifier so that the consumer can skip messages that originated from itself and avoid the echo loop.

// Inventory dual write subscriber, target platform side
async function syncShopifyInventoryToTarget(event: ShopifyInventoryEvent) {
  if (event.source === "target_platform") return  // skip echoes

  const variant = await targetClient.variants.findBySku(event.sku)
  if (!variant) return  // not in scope for this brand

  const currentTargetQty = await targetClient.inventory.getOnHand(variant.id, event.location_id)
  if (currentTargetQty === event.new_quantity) return  // already in sync

  await targetClient.inventory.setOnHand({
    variant_id: variant.id,
    location_id: event.location_id,
    quantity: event.new_quantity,
    source: "shopify_plus",  // tag for the reverse subscriber
    correlation_id: event.event_id,
  })

  await reconciliationLog.record({
    entity: "inventory",
    sku: event.sku,
    from_qty: currentTargetQty,
    to_qty: event.new_quantity,
    source_event_id: event.event_id,
    propagation_ms: Date.now() - event.timestamp,
  })
}

The reconciliation log is the audit trail that the team uses to investigate drift. Every dual write produces a reconciliation log entry, and an hourly batch job compares the inventory totals between the two systems and flags any SKU/location pair that differs. The tolerance is zero on inventory and one cent on monetary fields (to absorb rounding differences in tax calculation that are not actually drift). A drift event triggers a Slack alert to the migration team and pauses checkout cutover for the affected SKUs until the drift is investigated.

Cutover Patterns: Hot, Warm, and Cold

There are three viable cutover patterns for the checkout switch, and the brand profile decides which one fits.

The hot cutover switches all traffic in a single deployment, typically during a four hour low traffic window. This is the fastest pattern (the dual write window ends at the cutover) and the highest risk. Use the hot cutover for brands with low order volume (under 200 orders per day) where a four hour outage on the new checkout would be a containable incident rather than a business stopping event. The advantage is that the team only has to operate one checkout at a time after cutover; the disadvantage is that the rollback story is "fail forward and accept order volume loss during the rollback window."

The warm cutover shifts traffic in stages, typically 10 percent on day one, 50 percent on day three, 100 percent on day seven. This is the default pattern for mid market brands ($20M to $200M GMV) and the one that produces the cleanest production data on whether the target platform behaves under real load. The trade off is that the dual write window stretches across the staged cutover (typically two weeks rather than the four hour hot cutover window), and the operational burden on the migration team is higher because they are operating both checkouts in parallel.

The cold cutover (sometimes called a regional cutover) launches the target platform in a new region or a new brand altogether, leaves the Shopify Plus storefront running for the existing region, and never actually cuts over the existing region. This is the pattern that fits when the brand cannot tolerate any risk to the existing GMV and has appetite for parallel operations indefinitely. The cost is that the brand runs two commerce stacks forever, which is a different operational shape than a migration.

When This Applies to Your Stack

The migration off Shopify Plus to an open source backend is the right call for: brands that have outgrown Shopify Plus's customization boundaries (typically $50M+ GMV with deep custom Function logic), brands with B2B requirements that exceed Shopify Plus B2B (Vendure is the strongest fit here), brands with regulatory or sovereignty requirements that demand on premise deployment (Saleor and Vendure both support this; Medusa Cloud does not), and brands building proprietary commerce IP that they want to own rather than lease.

It is the wrong call for: brands under $20M GMV where the engineering cost of platform ownership exceeds the SaaS subscription cost, brands without an engineering team capable of operating a self hosted commerce backend (this is most brands), and brands where the Shopify Plus ecosystem (apps, integrations, theme marketplace) provides material value that the open source platforms cannot replicate.

If your team is evaluating whether to migrate off Shopify Plus, we work on platform migration projects for enterprise brands. The hardest parts (the migration order, the dual write reconciliation, the rollback story, the integration cutover sequence) are pattern work that benefits from teams who have shipped this several times across the three target platforms.

FAQ

Can the migration happen without a dual write window?

It can, on small brands with low order volume. The pattern is a hard cutover during a maintenance window with all orders frozen, the data migrated in one batch, and the target platform brought up live. The maintenance window is typically 8 to 24 hours depending on data volume. We do not recommend this pattern for brands above $20M GMV because the order loss during the maintenance window is too expensive, and the rollback story (revert to Shopify Plus and lose any orders placed on the target platform during the brief live window) is too painful.

Which platform has the most mature migration tooling from Shopify Plus in 2026?

Saleor has the most polished first party migration suite, including a CLI that handles product, customer, and order import with field mapping configurable per brand. Medusa has a community CLI (medusa-import-shopify) that covers products and customers well but requires custom work for orders. Vendure has the least developed migration tooling and typically requires a custom import script for any non trivial brand. None of the three handle the integration migration (NetSuite, Stripe, fulfillment partners) automatically; that work is custom regardless of platform.

How long does a typical migration take?

A small brand (under $10M GMV, light customization) on a warm cutover takes 12 to 16 weeks. A mid market brand ($50M to $150M GMV, moderate customization, NetSuite integration) on a warm cutover takes 6 to 9 months. An enterprise brand ($200M+ GMV, heavy Function logic, custom checkout, ERP integration) takes 12 to 18 months. The customization volume on the Shopify Plus side is the dominant variable, not the GMV.

What happens to the Shopify Plus theme during the migration?

It gets discarded. None of the three target platforms run Liquid templates, and the storefront has to be rebuilt against the target platform's API. The rebuild is usually the longest single workstream in the migration and the one that benefits most from starting early. Brands that already run a Hydrogen or Remix storefront against Shopify save the most rebuild work; the rebuild is a re-point of the data layer rather than a ground up storefront build.

[ 02 ] — Keep Reading

More from the lab.

Aug 2, 2026 Headless Commerce

Segment vs RudderStack vs mParticle: Customer Data Platform for Headless Commerce (2026)

A customer data platform is the least visible and most load-bearing piece of a headless commerce stack. It is the layer that captures every event from the storefront, the mobile app, and the backend, resolves those events into a single view of the customer, and fans them out to analytics, email, ads, and the warehouse. Because it sits in the middle of everything, the CDP choice quietly decides three things that are expensive to change later: whether you own your customer data or rent access to it, how much of your compliance and consent surface lives inside a vendor versus your own infrastructure, and how your bill scales as event volume grows, which in commerce it always does. Segment, RudderStack, and mParticle are the three platforms most enterprise commerce teams shortlist, and they embody genuinely different philosophies rather than being feature-for-feature clones. Segment is the managed incumbent optimized for time to value. RudderStack is the warehouse-first, self-hostable challenger built for teams that want to own the pipeline. mParticle is the mobile-heavy enterprise option with the deepest identity and audience tooling. For a headless architecture, where the storefront is decoupled and events originate from several surfaces at once, the differences in how each handles server-side collection, identity resolution, and pricing at volume are what separate a clean integration from a costly one. This post lays out those differences and the decision framework that follows from them.

Jul 30, 2026 Headless Commerce

Multi Currency and Cross Border Pricing in Headless Commerce: Duties, Rounding, and the Presentment Problem (2026)

Selling internationally on a packaged storefront is mostly a settings screen: turn on the currencies, let the platform convert, and the theme shows the right number. Go headless and the convenience disappears, because now your own frontend is responsible for asking the API for a price in the customer's currency, displaying it with the correct rounding, carrying that currency all the way through the cart and into checkout, and reconciling what the customer paid against what the store settles in. Each of those steps has a way to go wrong that a themed store never exposed you to, and the most common one is subtle: the storefront requests a presentment currency and the API quietly returns the amount in the store's base currency anyway, so the customer sees a euro sign in front of a dollar number. Add duties and import taxes on a cross border order and the surface expands again, because now the total the customer sees at checkout has to include or exclude a duty depending on whether you sell delivered duty paid or delivered duty unpaid, and getting that wrong means either a surprised customer or a margin you did not plan to give away. This post is about the architecture that keeps a headless international store honest: which system owns the converted price, how presentment currency actually flows through the Storefront API, why rounding is a real rule and not a rounding error, and how duties change the checkout total.

Jul 29, 2026 Headless Commerce

Migrating from Salesforce Commerce Cloud to Headless Shopify Plus: A Replatforming Playbook (2026)

The decision to leave Salesforce Commerce Cloud is usually made on cost and velocity, and by the time it reaches engineering it has hardened into a deadline. That is where replatforming projects go wrong, because moving from SFCC to a headless Shopify Plus stack is not a migration in the copy the data and flip the switch sense; it is a rebuild of the parts of your commerce logic that lived inside SFCC cartridges and pipelines, wrapped around a data migration that is the easy part by comparison. SFCC gave you a monolith where the storefront, the business logic, and the platform were fused, and headless Shopify Plus deliberately unfuses them: Shopify becomes the commerce engine behind an API, and the storefront becomes your own application. Everything that made SFCC feel complete, the cartridge ecosystem, the pipeline customizations, the server side rendering baked in, becomes something you now own explicitly. This playbook walks the migration in the order that actually de-risks it: what maps cleanly from the SFCC data model to Shopify, what has to be rebuilt rather than ported, how to protect the SEO equity that a careless cutover destroys, and the sequencing that lets you move without a big bang launch. The projects that fail treat this as a data problem. The ones that succeed treat it as a rebuild with a data migration attached.

Ready when you are

Want to discuss this topic?

Start a Conversation