E-Commerce Platforms & Commerce Systems
A comprehensive guide to building modern e-commerce platforms. Learn about headless commerce architecture, Vendure, catalog management, pricing strategies, checkout optimization, and scaling for high-volume sales.
The State of E-Commerce Technology
Most e-commerce platforms are stuck in the past. Monolithic systems where every change risks breaking checkout. Templates that look like every other store. Performance that crawls during peak traffic.
We've migrated enough stores off legacy platforms to know the pain. Shopify's limitations hit fast once you grow. Magento's complexity becomes a liability. WooCommerce was never meant for serious scale.
Modern commerce needs a modern approach: headless architecture, clean data models, and systems built for your business—not the platform's assumptions.
Your commerce platform should adapt to your business, not force your business to adapt to it.
This isn't about technology for technology's sake. It's about building commerce systems that actually scale, perform, and evolve with your needs.
Why Headless Commerce?
In traditional e-commerce, the frontend and backend are fused together. Change the checkout flow? Risk breaking inventory. Add a mobile app? Rebuild everything. Integrate with a marketplace? Good luck.
Headless separates the commerce engine from the presentation layer. The backend handles products, orders, inventory, pricing. The frontend is completely independent—build what you want, how you want.
| Traditional | Headless |
|---|---|
| Frontend locked to platform | Any frontend: web, mobile, kiosk, IoT |
| Changes risk entire system | Change frontend without touching backend |
| Limited customization | Total control over experience |
| Platform dictates UX | You dictate UX |
| One channel | Omnichannel native |
Real Example: Multi-Channel Retailer
A client sells through web, mobile app, in-store kiosks, and Amazon. With a traditional platform, that's four separate systems with sync nightmares.
With headless:
┌─────────────────────────────────────────────┐
│ Commerce Engine (Vendure) │
│ Products │ Inventory │ Orders │ Customers │
└─────────────────────────────────────────────┘
│ │ │
┌────────┴───┐ ┌───┴───┐ ┌───┴────┐
│ Web │ │ Mobile │ │ Amazon │
│ (Next.js)│ │ (React │ │ (API) │
│ │ │ Native)│ │ │
└────────────┘ └────────┘ └────────┘
One source of truth for inventory. One place for orders. Multiple ways to sell.
Our Platform: Vendure
We build commerce systems on Vendure. Here's why.
Open Source & Extensible
Vendure is open source, written in TypeScript, built on Node.js. You own the code. You can modify anything. No license fees eating your margins.
Built for Customization
Every business has unique requirements. Vendure's plugin architecture lets us add anything without hacking the core.
// Example: Custom plugin for B2B pricing
@VendurePlugin({
imports: [PluginCommonModule],
providers: [B2BPricingService],
configuration: config => {
config.customFields.Customer.push({
name: 'priceGroup',
type: 'string',
options: [
{ value: 'retail' },
{ value: 'wholesale' },
{ value: 'distributor' }
]
});
return config;
}
})
export class B2BPricingPlugin {}
GraphQL Native
Vendure uses GraphQL by default. Clients request exactly what they need—no over-fetching, no under-fetching.
query ProductPage($slug: String!) {
product(slug: $slug) {
id
name
description
variants {
id
name
price
stockLevel
options {
name
value
}
}
assets {
source
preview
}
}
}
Vendure vs. Alternatives
| Aspect | Vendure | Shopify | Magento | commercetools |
|---|---|---|---|---|
| Cost | Open source | Monthly fee + % | License + hosting | Usage-based |
| Ownership | Full | Platform-owned | License | Platform |
| Customization | Unlimited | Limited | Complex | API only |
| Tech Stack | TypeScript/Node | Locked | PHP | Any |
| Hosting | Your choice | Shopify only | Your choice | Cloud only |
| Best For | Custom builds | Quick start | Enterprise (legacy) | Large enterprise |
Catalog Management
Your product catalog is the foundation. Get it wrong, and everything downstream suffers.
Product Data Model
Products aren't simple. They have variants, options, attributes, categories, and relationships. We model this properly from the start.
// Product structure example
interface Product {
id: string;
name: string;
slug: string;
description: string;
// Variants: actual purchasable items
variants: ProductVariant[];
// Options: what differentiates variants
options: ProductOption[]; // Size, Color, etc.
// Attributes: searchable/filterable properties
attributes: ProductAttribute[]; // Brand, Material, etc.
// Categories: hierarchical organization
categories: Category[];
// Assets: images, videos, documents
assets: Asset[];
// SEO
seo: {
title: string;
description: string;
keywords: string[];
};
}
interface ProductVariant {
id: string;
sku: string;
price: Money;
stockLevel: number;
options: SelectedOption[]; // { size: 'L', color: 'Blue' }
}
Faceted Navigation
Customers expect to filter products by multiple attributes simultaneously. This requires proper facet indexing.
| Facet Type | Example | Implementation |
|---|---|---|
| Category | Electronics > Phones | Hierarchical, breadcrumbs |
| Range | Price €100-€200 | Min/max aggregation |
| Multi-select | Colors: Red, Blue | Array intersection |
| Boolean | In stock only | Simple filter |
// Search with facets
const results = await search({
term: 'phone',
facets: {
category: ['electronics', 'phones'],
brand: ['Apple', 'Samsung'],
priceRange: { min: 500, max: 1000 },
inStock: true
},
sort: { field: 'price', direction: 'ASC' },
pagination: { page: 1, limit: 24 }
});
// Results include facet counts for UI
results.facets = {
brand: [
{ value: 'Apple', count: 24 },
{ value: 'Samsung', count: 18 }
],
priceRanges: [
{ range: '500-750', count: 20 },
{ range: '750-1000', count: 22 }
]
};
Pricing Architecture
Pricing is where commerce gets complex. Simple retail pricing is the easy case. Real businesses need much more.
Pricing Models We Support
| Model | Use Case | Complexity |
|---|---|---|
| Fixed Price | Standard retail | Low |
| Customer Group Pricing | B2B, wholesale | Medium |
| Volume Pricing | Buy more, save more | Medium |
| Dynamic Pricing | Demand-based, competitor-aware | High |
| Contract Pricing | Enterprise agreements | High |
| Promotional Pricing | Sales, coupons, bundles | Medium |
// Complex pricing calculation
async function calculatePrice(
product: Product,
customer: Customer,
quantity: number,
context: PriceContext
): Promise<Money> {
let price = product.basePrice;
// Customer group discount
if (customer.priceGroup === 'wholesale') {
price = applyDiscount(price, customer.discount);
}
// Volume pricing
const volumeTier = getVolumeTier(quantity);
if (volumeTier) {
price = applyVolumeDiscount(price, volumeTier);
}
// Contract pricing override
const contractPrice = await getContractPrice(product, customer);
if (contractPrice && contractPrice < price) {
price = contractPrice;
}
// Active promotions
const promos = await getActivePromotions(product, context);
price = applyPromotions(price, promos);
return price;
}
Multi-Currency
International commerce requires proper currency handling. Not just conversion, but localized pricing.
// Currency configuration
const currencyConfig = {
EUR: { default: true, symbol: '€', position: 'before' },
USD: { symbol: '$', position: 'before' },
GBP: { symbol: '£', position: 'before' },
CHF: { symbol: 'CHF', position: 'after' }
};
// Price per channel/locale
const productPricing = {
'de-DE': { currency: 'EUR', price: 9900 }, // €99.00
'en-US': { currency: 'USD', price: 10900 }, // $109.00
'en-GB': { currency: 'GBP', price: 8500 } // £85.00
};
Checkout Optimization
Checkout is where money is made or lost. Every friction point costs conversions.
The Checkout Flow
Cart → Customer Info → Shipping → Payment → Confirmation
│ │ │ │
└─ Save └─ Validate └─ Quote └─ Process
state address rates payment
Reducing Friction
| Problem | Solution | Impact |
|---|---|---|
| Too many steps | Single-page checkout | +15% conversion |
| No guest checkout | Guest option prominent | +30% new customers |
| Slow loading | Optimized assets, edge caching | +10% conversion |
| Hidden costs | Show shipping early | -25% cart abandonment |
| Limited payment options | Multiple methods | +10% conversion |
// Guest checkout with account creation option
const checkoutFlow = {
step1_cart: {
showSummary: true,
showEstimatedShipping: true, // No surprises later
showPromoCode: true
},
step2_info: {
guestCheckout: true, // Don't force registration
addressAutocomplete: true,
rememberDetails: true
},
step3_shipping: {
showAllOptions: true,
preselect: 'cheapest', // or 'fastest'
showDeliveryDates: true
},
step4_payment: {
methods: ['card', 'paypal', 'klarna', 'applepay', 'googlepay'],
savePaymentMethod: true, // For logged-in users
expressCheckout: true
}
};
Payment Integration
We integrate with multiple payment providers. No single point of failure, no vendor lock-in.
| Provider | Best For | Features |
|---|---|---|
| Stripe | General purpose | Cards, wallets, subscriptions |
| PayPal | Consumer trust | Pay later, buyer protection |
| Klarna | Buy now, pay later | Installments, increased AOV |
| Adyen | Enterprise, global | All methods, local acquiring |
Inventory & Fulfillment
Selling products you don't have ruins customer trust. Inventory management needs to be bulletproof.
Inventory Tracking
interface InventoryLocation {
id: string;
name: string; // 'Main Warehouse', 'Store Berlin'
type: 'warehouse' | 'store' | 'dropship';
stock: Map<VariantId, StockLevel>;
}
interface StockLevel {
available: number; // Can sell now
reserved: number; // In carts, pending orders
incoming: number; // On purchase orders
threshold: number; // Reorder point
}
// Check availability across locations
async function checkAvailability(
variant: ProductVariant,
quantity: number,
shipTo: Address
): Promise<AvailabilityResult> {
const locations = await getLocationsByProximity(shipTo);
for (const location of locations) {
const stock = await getStock(variant, location);
if (stock.available >= quantity) {
return {
available: true,
location,
deliveryEstimate: calculateDelivery(location, shipTo)
};
}
}
return { available: false, backorderDate: await getBackorderDate(variant) };
}
Order Lifecycle
Created → Paid → Processing → Shipped → Delivered
│ │ │ │ │
│ │ │ │ └─ Complete
│ │ │ └─ Tracking update
│ │ └─ Pick, pack, ship
│ └─ Payment captured
└─ Inventory reserved
Scaling for Traffic
Black Friday traffic kills unprepared stores. We build for 10x your normal load.
Performance Architecture
| Layer | Strategy | Implementation |
|---|---|---|
| CDN | Cache static assets, pages | CloudFront, Fastly |
| API | Cache responses, rate limit | Redis, API gateway |
| Database | Read replicas, connection pooling | PostgreSQL, PgBouncer |
| Search | Dedicated search cluster | Elasticsearch, Algolia |
| Queue | Async processing | Redis, SQS |
// Caching strategy
const cacheConfig = {
// Product pages: cache with revalidation
productPage: {
ttl: 3600, // 1 hour
staleWhileRevalidate: 86400, // Serve stale for 24h while refreshing
tags: ['products', 'product:${id}']
},
// Cart/checkout: no cache
checkout: {
cache: false
},
// Category pages: short cache
categoryPage: {
ttl: 300, // 5 minutes
staleWhileRevalidate: 3600
}
};
Load Testing
We don't guess about performance. We test before launch.
# Load test example with k6
k6 run --vus 500 --duration 10m loadtest.js
# Results we target:
# - P95 response time < 200ms
# - Error rate < 0.1%
# - No degradation under 10x normal load
B2B Commerce
B2B commerce has different requirements than B2C. We handle both.
| B2C | B2B |
|---|---|
| Fixed prices | Negotiated prices |
| Individual buyers | Buyer organizations |
| Immediate payment | Net terms (30/60/90) |
| Simple checkout | Approval workflows |
| Guest checkout | Account required |
// B2B organization structure
interface Organization {
id: string;
name: string;
taxId: string;
creditLimit: Money;
paymentTerms: 'net30' | 'net60' | 'net90';
// Multiple buyers per org
buyers: Buyer[];
// Approval workflows
approvalRules: ApprovalRule[];
// Custom pricing
priceList: PriceList;
// Addresses
shippingAddresses: Address[];
billingAddress: Address;
}
// Approval workflow
interface ApprovalRule {
condition: 'orderTotal > 5000' | 'newVendor';
approvers: User[];
requireAll: boolean;
}
Getting Started
Here's how we typically build commerce systems:
Phase 1: Discovery & Design (2-4 weeks)
- Understand your business model, products, customers
- Map out integrations (ERP, WMS, PIM)
- Design data model and workflows
- Plan migration strategy if applicable
Phase 2: Platform Build (8-12 weeks)
- Vendure setup and customization
- Custom plugins for your requirements
- Integration development
- Admin interface configuration
Phase 3: Frontend Build (6-10 weeks)
- Design implementation
- Performance optimization
- Mobile responsiveness
- Accessibility compliance
Phase 4: Launch & Optimize
- Load testing and hardening
- Migration execution
- Launch support
- Ongoing optimization
Conclusion
E-commerce technology should enable your business, not constrain it. With headless architecture and platforms like Vendure, you get full control over your commerce experience while maintaining the flexibility to evolve.
The best commerce platform is invisible to customers. They just get a fast, smooth experience that makes buying easy.
We've built commerce systems handling millions in transactions. If you're outgrowing your current platform or starting fresh, we'd be happy to discuss what modern commerce could look like for your business.
Topics covered
Ready to implement agentic AI?
Our team specializes in building production-ready AI systems. Let's discuss how we can help you leverage agentic AI for your enterprise.
Start a conversation