Custom E-commerce Platform Architecture: The Build vs Buy Decision Framework
Most e-commerce founders face the same choice at $5M GMV: keep building on Shopify or invest in a custom platform. This is the framework we use to analyze that decision — and what the architecture looks like when build is the right answer.',

The question comes up in almost every e-commerce engagement we take on past a certain scale: should we build a custom e-commerce platform, or continue on Shopify? The right answer depends on specifics, but the factors that determine it are consistent enough that we have developed a framework for thinking through it.
When staying on Shopify is clearly right
Below $2M annual GMV, the cost and complexity of a custom platform almost never makes business sense. Shopify's total cost of ownership — including apps, development, and Shopify Plus — is significantly lower than the engineering investment required to build and maintain a comparable custom system.
Also stay on Shopify if:
- Your catalog is standard — products with standard variants (size, color), no complex configuration logic
- Your checkout is standard — no multi-step quoting, no custom pricing rules beyond simple discounts
- Your operational complexity is manageable within Shopify's admin — your team is not fighting the tool to do their daily work
- Your technical team is small — maintaining a custom platform requires dedicated engineering bandwidth that a 2-3 person team cannot spare
When a custom platform becomes the right decision
The signals that the platform is holding you back:
- Complex product configuration: Products requiring customer input beyond variant selection — custom engravings, configurable bundles, made-to-order items with specifications that affect price. Shopify's product model does not handle this cleanly, and the workarounds accumulate technical debt.
- Custom pricing logic: B2B pricing with customer-specific price lists, volume discounts that depend on account history, dynamic pricing that responds to inventory levels or competitor pricing. Shopify Scripts and app-layer pricing logic become unwieldy at this complexity.
- High-volume catalog management: Catalogs with hundreds of thousands of SKUs, frequent bulk updates, complex inventory rules across multiple warehouses. Shopify's API rate limits and bulk operation patterns become real constraints at scale.
- Non-standard fulfillment: Multiple warehouses with intelligent routing, dropship vendors with their own feeds, partial shipment logic, custom SLAs by product category or customer tier. These require workflow logic that Shopify cannot express natively.
- Deep ERP integration: If your business runs on a custom ERP, Oracle, or SAP, and the integration between your commerce layer and ERP has become a maintenance burden — syncing inventory, orders, and customer data through Shopify's API has limits that eventually become blockers.
- Unit economics at scale: At high GMV volumes, Shopify Plus fees plus the cost of apps that together replace what a custom platform would do natively can approach or exceed the cost of maintaining a custom system.
The hybrid architecture (often the right middle ground)
Full custom e-commerce platforms are not the only alternative to pure Shopify. A hybrid architecture — Shopify for customer-facing storefront and checkout, custom backend for business logic, catalog management, and ERP integration — captures many of the benefits of custom without replacing Shopify's genuine strengths.
We typically recommend this hybrid before a full custom build because Shopify's storefront, checkout, and payment processing are genuinely excellent and would be expensive to replicate. The custom layer handles the parts where Shopify's model does not fit.
Custom platform service decomposition
When the decision is to build a custom platform, it decomposes into these primary domains:
- Catalog service: Products, variants, categories, attributes, pricing rules. Owns the source of truth for product data.
- Inventory service: Stock levels by location, reservations, replenishment triggers, multi-warehouse routing.
- Order service: Order lifecycle from cart through fulfillment and returns. Coordinates with inventory and fulfillment services.
- Customer service: Accounts, addresses, order history, loyalty programs, price lists for B2B customers.
- Pricing service: Calculates final prices based on customer tier, volume, active promotions, and dynamic pricing rules.
- Checkout service: Cart management, address validation, shipping quotes, payment orchestration.
- Fulfillment service: Order routing to warehouses or dropship vendors, label generation, tracking updates.
- Search service: Product search and filtering, backed by Typesense or Elasticsearch.
Whether to implement these as fully separate microservices or modular components within a monolith depends on team size. For teams under 10 engineers, a well-structured monolith with clear module boundaries is almost always the right call — the operational overhead of microservices is not worth it at that scale. Design the module boundaries as if they were services so the split is clean when the time comes.
The catalog data model
The catalog schema is where most custom platforms get complicated. A simplified but illustrative version:
CREATE TABLE products (
id CHAR(36) PRIMARY KEY,
slug VARCHAR(255) UNIQUE NOT NULL,
name VARCHAR(500) NOT NULL,
status ENUM('active','draft','archived') DEFAULT 'draft',
created_at DATETIME(3) NOT NULL,
updated_at DATETIME(3) NOT NULL
);
CREATE TABLE product_variants (
id CHAR(36) PRIMARY KEY,
product_id CHAR(36) NOT NULL REFERENCES products(id),
sku VARCHAR(100) UNIQUE NOT NULL,
attr_size VARCHAR(50),
attr_color VARCHAR(50),
weight_g INT,
status ENUM('active','discontinued') DEFAULT 'active'
);
CREATE TABLE variant_prices (
variant_id CHAR(36) NOT NULL REFERENCES product_variants(id),
currency CHAR(3) NOT NULL DEFAULT 'USD',
base_price DECIMAL(12,2) NOT NULL,
compare_at DECIMAL(12,2),
PRIMARY KEY (variant_id, currency)
);
CREATE TABLE inventory_levels (
variant_id CHAR(36) NOT NULL REFERENCES product_variants(id),
location_id CHAR(36) NOT NULL,
quantity INT NOT NULL DEFAULT 0,
reserved INT NOT NULL DEFAULT 0,
PRIMARY KEY (variant_id, location_id)
);
The pricing engine
Pricing logic is the most business-specific part of any custom platform. The architecture that handles complexity without becoming unmaintainable: a rules engine with a defined evaluation order.
interface PricingRule {
priority: number
condition: (ctx: PricingContext) => boolean
apply: (basePrice: number, ctx: PricingContext) => number
}
const PRICING_RULES: PricingRule[] = [
{
priority: 100,
condition: (ctx) => ctx.customerTier === 'wholesale',
apply: (price) => price * 0.70,
},
{
priority: 90,
condition: (ctx) => ctx.quantity >= 100,
apply: (price) => price * 0.85,
},
// dynamic rules loaded from database for merchant-configured promotions
]
async function calculatePrice(ctx: PricingContext): Promise {
const basePrice = await getBasePrice(ctx.variantId, ctx.currency)
const dynamicRules = await db.pricingRules.findMany({ where: { isActive: true } })
const allRules = [...PRICING_RULES, ...dynamicRules].sort((a, b) => b.priority - a.priority)
let price = basePrice
for (const rule of allRules) {
if (rule.condition(ctx)) price = rule.apply(price, ctx)
}
return Math.round(price * 100) / 100
}
Search architecture
Product search on a custom platform requires a dedicated search engine — MySQL full-text search is not adequate for faceted filtering, autocomplete, and ranking at scale. We use Typesense for most projects: self-hostable, fast, and operationally simpler than Elasticsearch.
The search service maintains a near-real-time index of the catalog, updated via events as products change. The key schema fields for faceted filtering:
{
"name": "products",
"fields": [
{"name": "id", "type": "string"},
{"name": "name", "type": "string"},
{"name": "category", "type": "string", "facet": true},
{"name": "brand", "type": "string", "facet": true},
{"name": "attr_size", "type": "string[]", "facet": true},
{"name": "attr_color", "type": "string[]", "facet": true},
{"name": "price", "type": "float", "facet": true},
{"name": "in_stock", "type": "bool", "facet": true},
{"name": "popularity_score", "type": "float"}
],
"default_sorting_field": "popularity_score"
}
Order processing with event sourcing
Order state is complex and must be auditable. We use event sourcing for the order lifecycle — rather than mutating an order record, we append events (OrderPlaced, PaymentConfirmed, ItemShipped, etc.) to an immutable event log and derive current state from the event history. This gives a complete audit trail of every state transition, the ability to reconstruct the state of an order at any point in time, and a natural integration point for other services to subscribe to order events without polling.
Frontend: Next.js with edge caching
The storefront is typically Next.js with aggressive edge caching for catalog pages and server-side rendering for user-specific pages (cart, account, checkout). The key caching consideration: product pages are mostly static (same for all users) but show real-time inventory and pricing, which is user- or tier-specific. We separate the static content from the dynamic parts using Next.js component composition and fetch inventory and pricing client-side after the static shell loads.
Migration strategy from Shopify
A strangler fig migration works well: launch the custom platform for new categories or customer segments first, then migrate existing segments one by one, keeping Shopify running until each segment is fully migrated. This avoids a big-bang cutover that puts the entire business at risk.
Plan for the migration to take 3-6 months for a moderately complex catalog. The migration work is rarely the platform build — it is the data cleaning, mapping, and validation that takes longer than expected.
If you are evaluating whether a custom platform is the right next step for your e-commerce business, we can help you work through the specific factors in your situation. The right answer depends heavily on your catalog complexity, operational model, and growth trajectory, and we have done this analysis enough times to know which factors actually determine the outcome.

Related service
E-commerce Software Development
Custom marketplaces, D2C storefronts, and post-sale platforms built for Amazon and eBay sellers.
Written by
Founder & CEO
Gaurang Ghinaiya is the Founder & CEO of Nexios Technologies. He is passionate about building innovative software solutions that drive business growth. With years of experience in technology leadership, he guides teams toward excellence.