The Dutch e-commerce market is booming, with online retail sales exceeding €30 billion annually. Major players like Bol.com and Coolblue have set the standard for customer experience, requiring infrastructure that can handle millions of visitors during peak events like Black Friday, Sinterklaas, and Cyber Monday. Amazon Web Services (AWS) provides the perfect foundation for building scalable, secure, and performant e-commerce platforms.
Why AWS for E-commerce?
The e-commerce landscape demands infrastructure that can scale from handling hundreds to millions of concurrent users within minutes. Traditional hosting solutions struggle with this elasticity, leading to site crashes during peak shopping periods, lost revenue, and damaged brand reputation. AWS offers a comprehensive suite of services specifically designed for e-commerce workloads:
Scalability: Auto Scaling groups can automatically add or remove compute capacity based on traffic patterns, ensuring your site remains responsive during unexpected traffic spikes.
Global Delivery: CloudFront’s edge locations across Europe and worldwide deliver content with millisecond latency, crucial for converting international customers.
Security and Compliance: AWS services are compliant with PCI-DSS requirements for payment processing, GDPR for European customer data, and provide built-in DDoS protection through AWS Shield.
Cost Optimization: Pay-per-use pricing means you only pay for the resources you consume, avoiding over-provisioning for peak capacity that sits idle most of the year.
Leading Dutch retailers have proven this approach works. When Coolblue expanded to Belgium and Germany, they leveraged AWS’s multi-region capabilities to maintain consistent performance across borders. Similarly, smaller Dutch fashion retailers and specialty stores have used AWS to compete with larger players by delivering enterprise-grade customer experiences without enterprise-scale infrastructure costs.
E-commerce Architecture on AWS
A modern e-commerce platform consists of several key components: product catalog management, shopping cart and session handling, order processing, payment integration, content delivery, and analytics. Let’s explore a reference architecture that handles these requirements efficiently.
Core Architecture Components
The foundation of a scalable e-commerce platform on AWS typically includes:
Frontend Delivery: Static website assets (HTML, CSS, JavaScript) hosted in Amazon S3 and distributed globally via CloudFront CDN. This approach dramatically reduces hosting costs while improving performance.
Application Layer: API Gateway provides the entry point for all backend operations, routing requests to Lambda functions (serverless) or containerized applications running on ECS/EKS (for more complex business logic).
Data Layer: DynamoDB for product catalog, shopping carts, and user sessions; RDS for transactional order data; ElastiCache for frequently accessed data like featured products and pricing.
Search and Discovery: Amazon OpenSearch Service powers product search, faceted navigation, and recommendations, providing the fast, relevant results customers expect.
Payment Processing: Integration with payment providers through secure Lambda functions, with sensitive data never touching your application servers.
Here’s a complete AWS CDK example that implements the core infrastructure:
import * as cdk from 'aws-cdk-lib';
import * as s3 from 'aws-cdk-lib/aws-s3';
import * as cloudfront from 'aws-cdk-lib/aws-cloudfront';
import * as origins from 'aws-cdk-lib/aws-cloudfront-origins';
import * as dynamodb from 'aws-cdk-lib/aws-dynamodb';
import * as apigateway from 'aws-cdk-lib/aws-apigateway';
import * as lambda from 'aws-cdk-lib/aws-lambda';
import * as elasticache from 'aws-cdk-lib/aws-elasticache';
import * as ec2 from 'aws-cdk-lib/aws-ec2';
import { Construct } from 'constructs';
export class EcommerceStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
// VPC for ElastiCache and private resources
const vpc = new ec2.Vpc(this, 'EcommerceVPC', {
maxAzs: 2,
natGateways: 1,
});
// S3 bucket for static website assets
const websiteBucket = new s3.Bucket(this, 'WebsiteBucket', {
websiteIndexDocument: 'index.html',
publicReadAccess: false,
blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL,
removalPolicy: cdk.RemovalPolicy.RETAIN,
});
// CloudFront distribution with caching optimizations
const distribution = new cloudfront.Distribution(this, 'WebsiteDistribution', {
defaultBehavior: {
origin: new origins.S3Origin(websiteBucket),
viewerProtocolPolicy: cloudfront.ViewerProtocolPolicy.REDIRECT_TO_HTTPS,
cachePolicy: new cloudfront.CachePolicy(this, 'CachePolicy', {
defaultTtl: cdk.Duration.hours(24),
minTtl: cdk.Duration.minutes(1),
maxTtl: cdk.Duration.days(365),
cookieBehavior: cloudfront.CacheCookieBehavior.none(),
queryStringBehavior: cloudfront.CacheQueryStringBehavior.none(),
headerBehavior: cloudfront.CacheHeaderBehavior.allowList('CloudFront-Viewer-Country'),
}),
},
priceClass: cloudfront.PriceClass.PRICE_CLASS_100, // Europe and North America
});
// DynamoDB table for product catalog
const productTable = new dynamodb.Table(this, 'ProductTable', {
partitionKey: { name: 'productId', type: dynamodb.AttributeType.STRING },
billingMode: dynamodb.BillingMode.PAY_PER_REQUEST,
pointInTimeRecovery: true,
stream: dynamodb.StreamViewType.NEW_AND_OLD_IMAGES,
});
// Global Secondary Index for category-based queries
productTable.addGlobalSecondaryIndex({
indexName: 'CategoryIndex',
partitionKey: { name: 'category', type: dynamodb.AttributeType.STRING },
sortKey: { name: 'price', type: dynamodb.AttributeType.NUMBER },
});
// DynamoDB table for shopping carts
const cartTable = new dynamodb.Table(this, 'CartTable', {
partitionKey: { name: 'userId', type: dynamodb.AttributeType.STRING },
timeToLiveAttribute: 'expiryTime',
billingMode: dynamodb.BillingMode.PAY_PER_REQUEST,
});
// DynamoDB table for orders
const orderTable = new dynamodb.Table(this, 'OrderTable', {
partitionKey: { name: 'orderId', type: dynamodb.AttributeType.STRING },
sortKey: { name: 'timestamp', type: dynamodb.AttributeType.NUMBER },
billingMode: dynamodb.BillingMode.PAY_PER_REQUEST,
pointInTimeRecovery: true,
});
// ElastiCache Redis cluster for session and product caching
const subnetGroup = new elasticache.CfnSubnetGroup(this, 'CacheSubnetGroup', {
description: 'Subnet group for Redis cache',
subnetIds: vpc.privateSubnets.map(subnet => subnet.subnetId),
});
const securityGroup = new ec2.SecurityGroup(this, 'CacheSecurityGroup', {
vpc,
description: 'Security group for Redis cache',
allowAllOutbound: true,
});
const redisCache = new elasticache.CfnReplicationGroup(this, 'RedisCache', {
replicationGroupDescription: 'Redis cache for e-commerce',
engine: 'redis',
cacheNodeType: 'cache.r6g.large',
numCacheClusters: 2,
automaticFailoverEnabled: true,
cacheSubnetGroupName: subnetGroup.ref,
securityGroupIds: [securityGroup.securityGroupId],
multiAzEnabled: true,
});
// Lambda function for product API
const productFunction = new lambda.Function(this, 'ProductFunction', {
runtime: lambda.Runtime.NODEJS_18_X,
handler: 'index.handler',
code: lambda.Code.fromAsset('lambda/product'),
environment: {
PRODUCT_TABLE: productTable.tableName,
REDIS_ENDPOINT: redisCache.attrPrimaryEndPointAddress,
},
vpc: vpc,
timeout: cdk.Duration.seconds(30),
memorySize: 1024,
});
productTable.grantReadWriteData(productFunction);
securityGroup.addIngressRule(
ec2.Peer.securityGroupId(productFunction.connections.securityGroups[0].securityGroupId),
ec2.Port.tcp(6379),
'Allow Lambda to access Redis'
);
// Lambda function for cart operations
const cartFunction = new lambda.Function(this, 'CartFunction', {
runtime: lambda.Runtime.NODEJS_18_X,
handler: 'index.handler',
code: lambda.Code.fromAsset('lambda/cart'),
environment: {
CART_TABLE: cartTable.tableName,
PRODUCT_TABLE: productTable.tableName,
},
timeout: cdk.Duration.seconds(30),
});
cartTable.grantReadWriteData(cartFunction);
productTable.grantReadData(cartFunction);
// Lambda function for order processing
const orderFunction = new lambda.Function(this, 'OrderFunction', {
runtime: lambda.Runtime.NODEJS_18_X,
handler: 'index.handler',
code: lambda.Code.fromAsset('lambda/order'),
environment: {
ORDER_TABLE: orderTable.tableName,
CART_TABLE: cartTable.tableName,
PRODUCT_TABLE: productTable.tableName,
},
timeout: cdk.Duration.seconds(60),
});
orderTable.grantReadWriteData(orderFunction);
cartTable.grantReadWriteData(orderFunction);
productTable.grantReadData(orderFunction);
// API Gateway
const api = new apigateway.RestApi(this, 'EcommerceAPI', {
restApiName: 'E-commerce API',
description: 'API for e-commerce operations',
deployOptions: {
throttlingRateLimit: 1000,
throttlingBurstLimit: 2000,
metricsEnabled: true,
loggingLevel: apigateway.MethodLoggingLevel.INFO,
},
});
// API resources and methods
const products = api.root.addResource('products');
products.addMethod('GET', new apigateway.LambdaIntegration(productFunction));
const productById = products.addResource('{productId}');
productById.addMethod('GET', new apigateway.LambdaIntegration(productFunction));
const cart = api.root.addResource('cart');
cart.addMethod('GET', new apigateway.LambdaIntegration(cartFunction));
cart.addMethod('POST', new apigateway.LambdaIntegration(cartFunction));
cart.addMethod('PUT', new apigateway.LambdaIntegration(cartFunction));
cart.addMethod('DELETE', new apigateway.LambdaIntegration(cartFunction));
const orders = api.root.addResource('orders');
orders.addMethod('POST', new apigateway.LambdaIntegration(orderFunction));
orders.addMethod('GET', new apigateway.LambdaIntegration(orderFunction));
// CloudFront behavior for API
distribution.addBehavior('/api/*', new origins.RestApiOrigin(api), {
viewerProtocolPolicy: cloudfront.ViewerProtocolPolicy.HTTPS_ONLY,
cachePolicy: cloudfront.CachePolicy.CACHING_DISABLED,
allowedMethods: cloudfront.AllowedMethods.ALLOW_ALL,
});
// Outputs
new cdk.CfnOutput(this, 'DistributionDomain', {
value: distribution.distributionDomainName,
description: 'CloudFront distribution domain',
});
new cdk.CfnOutput(this, 'ApiEndpoint', {
value: api.url,
description: 'API Gateway endpoint',
});
new cdk.CfnOutput(this, 'RedisEndpoint', {
value: redisCache.attrPrimaryEndPointAddress,
description: 'Redis cache endpoint',
});
}
}
This CDK stack creates a production-ready e-commerce infrastructure with global content delivery, serverless API endpoints, and a highly scalable data layer.
Auto Scaling for Peak Traffic Events
Dutch consumers are highly engaged with seasonal shopping events. Black Friday sees traffic spikes of 300-500% compared to normal days, while Sinterklaas (early December) drives sustained high traffic for weeks. Your infrastructure must handle these patterns without manual intervention.
Traffic Pattern Analysis
Before implementing Auto Scaling, understand your traffic characteristics:
Predictable Spikes: Events like Black Friday, Sinterklaas, and summer sales happen on known dates. Use scheduled scaling to pre-warm capacity.
Unpredictable Surges: Viral social media posts, influencer mentions, or news coverage can drive sudden traffic. Reactive scaling handles these scenarios.
Regional Variations: Dutch shopping patterns differ from other markets. Sinterklaas is uniquely important in the Netherlands and Belgium, while Queen’s Day sales are Netherlands-specific.
Implementing Elastic Load Balancing and Auto Scaling
For containerized e-commerce applications, ECS with Auto Scaling provides the right balance of control and automation:
import * as ecs from 'aws-cdk-lib/aws-ecs';
import * as elbv2 from 'aws-cdk-lib/aws-elasticloadbalancingv2';
import * as autoscaling from 'aws-cdk-lib/aws-applicationautoscaling';
// ECS Cluster
const cluster = new ecs.Cluster(this, 'EcommerceCluster', {
vpc: vpc,
containerInsights: true,
});
// ECS Task Definition
const taskDefinition = new ecs.FargateTaskDefinition(this, 'AppTask', {
memoryLimitMiB: 2048,
cpu: 1024,
});
const container = taskDefinition.addContainer('app', {
image: ecs.ContainerImage.fromRegistry('your-registry/ecommerce-app:latest'),
logging: ecs.LogDrivers.awsLogs({ streamPrefix: 'ecommerce' }),
environment: {
PRODUCT_TABLE: productTable.tableName,
REDIS_ENDPOINT: redisCache.attrPrimaryEndPointAddress,
},
healthCheck: {
command: ['CMD-SHELL', 'curl -f http://localhost/health || exit 1'],
interval: cdk.Duration.seconds(30),
timeout: cdk.Duration.seconds(5),
retries: 3,
startPeriod: cdk.Duration.seconds(60),
},
});
container.addPortMappings({
containerPort: 8080,
protocol: ecs.Protocol.TCP,
});
// ECS Service
const service = new ecs.FargateService(this, 'EcommerceService', {
cluster,
taskDefinition,
desiredCount: 2,
minHealthyPercent: 50,
maxHealthyPercent: 200,
circuitBreaker: { rollback: true },
});
// Application Load Balancer
const alb = new elbv2.ApplicationLoadBalancer(this, 'ALB', {
vpc,
internetFacing: true,
});
const listener = alb.addListener('Listener', {
port: 443,
certificates: [certificate], // ACM certificate
});
const targetGroup = listener.addTargets('ECS', {
port: 8080,
targets: [service],
healthCheck: {
path: '/health',
interval: cdk.Duration.seconds(30),
timeout: cdk.Duration.seconds(5),
healthyThresholdCount: 2,
unhealthyThresholdCount: 3,
},
deregistrationDelay: cdk.Duration.seconds(30),
});
// Auto Scaling based on CPU and Request Count
const scaling = service.autoScaleTaskCount({
minCapacity: 2,
maxCapacity: 50,
});
scaling.scaleOnCpuUtilization('CpuScaling', {
targetUtilizationPercent: 70,
scaleInCooldown: cdk.Duration.seconds(60),
scaleOutCooldown: cdk.Duration.seconds(30),
});
scaling.scaleOnRequestCount('RequestScaling', {
requestsPerTarget: 1000,
targetGroup: targetGroup,
scaleInCooldown: cdk.Duration.seconds(60),
scaleOutCooldown: cdk.Duration.seconds(30),
});
// Scheduled scaling for Black Friday (last Friday of November)
scaling.scaleOnSchedule('BlackFridayScaleUp', {
schedule: autoscaling.Schedule.cron({
hour: '6',
minute: '0',
weekDay: 'FRI',
month: 'NOV',
}),
minCapacity: 30,
maxCapacity: 100,
});
scaling.scaleOnSchedule('BlackFridayScaleDown', {
schedule: autoscaling.Schedule.cron({
hour: '0',
minute: '0',
weekDay: 'SAT',
month: 'NOV',
}),
minCapacity: 2,
maxCapacity: 50,
});
// Scheduled scaling for Sinterklaas period (December 1-6)
scaling.scaleOnSchedule('SinterklaasScaleUp', {
schedule: autoscaling.Schedule.cron({
hour: '6',
minute: '0',
day: '1',
month: 'DEC',
}),
minCapacity: 20,
maxCapacity: 80,
});
scaling.scaleOnSchedule('SinterklaasScaleDown', {
schedule: autoscaling.Schedule.cron({
hour: '0',
minute: '0',
day: '7',
month: 'DEC',
}),
minCapacity: 2,
maxCapacity: 50,
});
This configuration ensures your application maintains performance during both predictable events (Black Friday, Sinterklaas) and unexpected traffic spikes.
CloudFront CDN for Global Delivery
Content Delivery Networks are essential for e-commerce performance. Studies show that 53% of mobile users abandon sites that take longer than 3 seconds to load, and a 100ms delay can reduce conversions by 7%. CloudFront delivers content from edge locations closest to your users, dramatically reducing latency.
CloudFront Architecture for E-commerce
An effective CloudFront setup for e-commerce includes multiple cache behaviors:
Static Assets: Images, CSS, JavaScript files cached for days or weeks with aggressive compression.
Product Pages: Dynamic content with shorter cache times (5-15 minutes) that updates when products change.
Personalized Content: User-specific content like shopping carts that bypasses caching entirely.
API Requests: Routed to your backend with connection pooling and keep-alive for efficiency.
Here’s an advanced CloudFront configuration optimized for Dutch and European customers:
import * as cloudfront from 'aws-cdk-lib/aws-cloudfront';
import * as origins from 'aws-cdk-lib/aws-cloudfront-origins';
import * as s3 from 'aws-cdk-lib/aws-s3';
// Origin Access Identity for secure S3 access
const originAccessIdentity = new cloudfront.OriginAccessIdentity(this, 'OAI');
websiteBucket.grantRead(originAccessIdentity);
// Custom cache policy for product pages
const productCachePolicy = new cloudfront.CachePolicy(this, 'ProductCachePolicy', {
cachePolicyName: 'ProductPageCache',
comment: 'Cache policy for product pages',
defaultTtl: cdk.Duration.minutes(15),
minTtl: cdk.Duration.minutes(1),
maxTtl: cdk.Duration.hours(24),
cookieBehavior: cloudfront.CacheCookieBehavior.allowList('session-id'),
queryStringBehavior: cloudfront.CacheQueryStringBehavior.allowList('variant', 'color', 'size'),
headerBehavior: cloudfront.CacheHeaderBehavior.allowList(
'CloudFront-Viewer-Country',
'CloudFront-Viewer-Currency',
'Accept-Language'
),
enableAcceptEncodingGzip: true,
enableAcceptEncodingBrotli: true,
});
// Response headers policy for security and performance
const responseHeadersPolicy = new cloudfront.ResponseHeadersPolicy(this, 'SecurityHeaders', {
comment: 'Security headers for e-commerce',
securityHeadersBehavior: {
contentTypeOptions: { override: true },
frameOptions: { frameOption: cloudfront.HeadersFrameOption.DENY, override: true },
referrerPolicy: {
referrerPolicy: cloudfront.HeadersReferrerPolicy.STRICT_ORIGIN_WHEN_CROSS_ORIGIN,
override: true
},
strictTransportSecurity: {
accessControlMaxAge: cdk.Duration.days(365),
includeSubdomains: true,
override: true,
},
xssProtection: { protection: true, modeBlock: true, override: true },
},
customHeadersBehavior: {
customHeaders: [
{ header: 'Cache-Control', value: 'public, max-age=900', override: false },
{ header: 'X-Country', value: 'NL', override: false },
],
},
});
// Main distribution
const distribution = new cloudfront.Distribution(this, 'EcommerceDistribution', {
comment: 'E-commerce website distribution',
priceClass: cloudfront.PriceClass.PRICE_CLASS_100, // Europe and North America
geoRestriction: cloudfront.GeoRestriction.allowlist('NL', 'BE', 'DE', 'FR', 'GB', 'US'),
// Default behavior for static assets
defaultBehavior: {
origin: new origins.S3Origin(websiteBucket, {
originAccessIdentity: originAccessIdentity,
}),
viewerProtocolPolicy: cloudfront.ViewerProtocolPolicy.REDIRECT_TO_HTTPS,
cachePolicy: cloudfront.CachePolicy.CACHING_OPTIMIZED,
compress: true,
responseHeadersPolicy: responseHeadersPolicy,
},
// Additional behaviors
additionalBehaviors: {
// Product images with long cache
'/images/*': {
origin: new origins.S3Origin(websiteBucket, {
originAccessIdentity: originAccessIdentity,
}),
viewerProtocolPolicy: cloudfront.ViewerProtocolPolicy.REDIRECT_TO_HTTPS,
cachePolicy: new cloudfront.CachePolicy(this, 'ImageCache', {
defaultTtl: cdk.Duration.days(30),
maxTtl: cdk.Duration.days(365),
minTtl: cdk.Duration.days(1),
}),
compress: true,
},
// Product pages with medium cache
'/products/*': {
origin: new origins.HttpOrigin(alb.loadBalancerDnsName, {
protocolPolicy: cloudfront.OriginProtocolPolicy.HTTPS_ONLY,
connectionAttempts: 3,
connectionTimeout: cdk.Duration.seconds(10),
readTimeout: cdk.Duration.seconds(30),
keepaliveTimeout: cdk.Duration.seconds(5),
}),
viewerProtocolPolicy: cloudfront.ViewerProtocolPolicy.REDIRECT_TO_HTTPS,
cachePolicy: productCachePolicy,
originRequestPolicy: cloudfront.OriginRequestPolicy.ALL_VIEWER,
compress: true,
responseHeadersPolicy: responseHeadersPolicy,
},
// Shopping cart - no caching
'/cart/*': {
origin: new origins.HttpOrigin(alb.loadBalancerDnsName, {
protocolPolicy: cloudfront.OriginProtocolPolicy.HTTPS_ONLY,
}),
viewerProtocolPolicy: cloudfront.ViewerProtocolPolicy.HTTPS_ONLY,
cachePolicy: cloudfront.CachePolicy.CACHING_DISABLED,
originRequestPolicy: cloudfront.OriginRequestPolicy.ALL_VIEWER,
allowedMethods: cloudfront.AllowedMethods.ALLOW_ALL,
},
// API endpoints - no caching
'/api/*': {
origin: new origins.RestApiOrigin(api),
viewerProtocolPolicy: cloudfront.ViewerProtocolPolicy.HTTPS_ONLY,
cachePolicy: cloudfront.CachePolicy.CACHING_DISABLED,
originRequestPolicy: cloudfront.OriginRequestPolicy.ALL_VIEWER,
allowedMethods: cloudfront.AllowedMethods.ALLOW_ALL,
},
},
// Custom error responses
errorResponses: [
{
httpStatus: 404,
responseHttpStatus: 404,
responsePagePath: '/404.html',
ttl: cdk.Duration.minutes(5),
},
{
httpStatus: 500,
responseHttpStatus: 500,
responsePagePath: '/500.html',
ttl: cdk.Duration.seconds(0),
},
],
// Enable logging
enableLogging: true,
logBucket: logBucket,
logFilePrefix: 'cloudfront/',
});
This CloudFront configuration optimizes caching strategies based on content type while maintaining security and performance.
Product Catalog with DynamoDB
DynamoDB is ideal for product catalogs due to its ability to scale to millions of items, handle unpredictable traffic spikes, and provide single-digit millisecond latency. Unlike traditional relational databases, DynamoDB’s NoSQL design allows flexible schemas that accommodate varying product attributes.
Schema Design for Product Catalog
Effective DynamoDB schema design requires thinking about access patterns first:
# Product catalog schema design
import boto3
from decimal import Decimal
from datetime import datetime
dynamodb = boto3.resource('dynamodb', region_name='eu-west-1')
product_table = dynamodb.Table('Products')
# Example product item
product_item = {
'productId': 'PROD-12345', # Partition key
'category': 'Electronics',
'subcategory': 'Laptops',
'brand': 'Dell',
'name': 'Dell XPS 15',
'description': 'Premium laptop with 15-inch display',
'price': Decimal('1299.99'),
'currency': 'EUR',
'stock': 45,
'warehouse': 'NL-AMSTERDAM',
'attributes': {
'processor': 'Intel i7-12700H',
'ram': '16GB',
'storage': '512GB SSD',
'display': '15.6-inch FHD',
'weight': '1.8kg',
},
'images': [
'https://cdn.example.com/products/prod-12345-1.jpg',
'https://cdn.example.com/products/prod-12345-2.jpg',
],
'rating': Decimal('4.5'),
'reviewCount': 127,
'tags': ['laptop', 'premium', 'business'],
'inStock': True,
'createdAt': int(datetime.now().timestamp()),
'updatedAt': int(datetime.now().timestamp()),
'SEO': {
'title': 'Dell XPS 15 Laptop - Premium Performance',
'description': 'Buy Dell XPS 15 with Intel i7, 16GB RAM, 512GB SSD',
'keywords': ['Dell XPS', 'laptop', 'premium laptop'],
},
}
# Insert product
product_table.put_item(Item=product_item)
# Query products by category (using GSI)
response = product_table.query(
IndexName='CategoryIndex',
KeyConditionExpression='category = :cat',
ExpressionAttributeValues={
':cat': 'Electronics'
}
)
# Query with price filtering
response = product_table.query(
IndexName='CategoryIndex',
KeyConditionExpression='category = :cat AND price BETWEEN :min AND :max',
ExpressionAttributeValues={
':cat': 'Electronics',
':min': Decimal('1000'),
':max': Decimal('1500'),
}
)
Implementing Product Search with Caching
Combine DynamoDB with ElastiCache Redis for optimal performance:
import boto3
import redis
import json
from decimal import Decimal
# Initialize clients
dynamodb = boto3.resource('dynamodb', region_name='eu-west-1')
product_table = dynamodb.Table('Products')
redis_client = redis.Redis(
host='your-redis-endpoint.cache.amazonaws.com',
port=6379,
decode_responses=True
)
class DecimalEncoder(json.JSONEncoder):
def default(self, obj):
if isinstance(obj, Decimal):
return float(obj)
return super(DecimalEncoder, self).default(obj)
def get_product(product_id):
"""Get product with Redis caching"""
cache_key = f'product:{product_id}'
# Try cache first
cached = redis_client.get(cache_key)
if cached:
print(f'Cache HIT for {product_id}')
return json.loads(cached)
# Cache miss - fetch from DynamoDB
print(f'Cache MISS for {product_id}')
response = product_table.get_item(Key={'productId': product_id})
if 'Item' not in response:
return None
product = response['Item']
# Store in cache for 1 hour
redis_client.setex(
cache_key,
3600,
json.dumps(product, cls=DecimalEncoder)
)
return product
def get_featured_products(category, limit=12):
"""Get featured products with list caching"""
cache_key = f'featured:{category}:{limit}'
# Try cache
cached = redis_client.get(cache_key)
if cached:
return json.loads(cached)
# Fetch from DynamoDB
response = product_table.query(
IndexName='CategoryIndex',
KeyConditionExpression='category = :cat',
FilterExpression='inStock = :stock AND rating >= :rating',
ExpressionAttributeValues={
':cat': category,
':stock': True,
':rating': Decimal('4.0'),
},
Limit=limit,
ScanIndexForward=False, # Descending order
)
products = response['Items']
# Cache for 15 minutes
redis_client.setex(
cache_key,
900,
json.dumps(products, cls=DecimalEncoder)
)
return products
def update_product_price(product_id, new_price):
"""Update price and invalidate cache"""
# Update in DynamoDB
product_table.update_item(
Key={'productId': product_id},
UpdateExpression='SET price = :price, updatedAt = :time',
ExpressionAttributeValues={
':price': Decimal(str(new_price)),
':time': int(datetime.now().timestamp()),
}
)
# Invalidate cache
redis_client.delete(f'product:{product_id}')
# Also invalidate category caches
product = product_table.get_item(Key={'productId': product_id})['Item']
pattern = f'featured:{product["category"]}:*'
for key in redis_client.scan_iter(match=pattern):
redis_client.delete(key)
print(f'Updated price for {product_id} and invalidated caches')
This implementation provides millisecond response times for frequently accessed products while ensuring data consistency when prices or inventory changes.
Payment Processing and PCI-DSS Compliance
Payment security is paramount for e-commerce. The Payment Card Industry Data Security Standard (PCI-DSS) requires strict controls around cardholder data. AWS provides tools to build PCI-DSS compliant payment flows without storing sensitive card data in your systems.
Tokenization Architecture
Modern e-commerce platforms use payment tokenization, where sensitive card data never touches your servers:
// Lambda function for payment processing with Stripe
import { APIGatewayProxyHandler } from 'aws-lambda';
import Stripe from 'stripe';
import { DynamoDB } from 'aws-sdk';
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!, {
apiVersion: '2023-10-16',
});
const dynamodb = new DynamoDB.DocumentClient();
const ORDER_TABLE = process.env.ORDER_TABLE!;
export const handler: APIGatewayProxyHandler = async (event) => {
try {
const { orderId, paymentMethodId, amount, currency, customerId } = JSON.parse(event.body!);
// Create payment intent
const paymentIntent = await stripe.paymentIntents.create({
amount: Math.round(amount * 100), // Convert to cents
currency: currency || 'eur',
payment_method: paymentMethodId,
customer: customerId,
confirm: true,
metadata: {
orderId: orderId,
source: 'ecommerce-website',
},
// Require 3D Secure authentication for EU regulations
payment_method_options: {
card: {
request_three_d_secure: 'automatic',
},
},
});
// Update order with payment status
await dynamodb.update({
TableName: ORDER_TABLE,
Key: { orderId },
UpdateExpression: 'SET paymentStatus = :status, paymentId = :paymentId, updatedAt = :time',
ExpressionAttributeValues: {
':status': paymentIntent.status,
':paymentId': paymentIntent.id,
':time': Date.now(),
},
}).promise();
// Return client secret for 3DS confirmation if needed
return {
statusCode: 200,
headers: {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*',
},
body: JSON.stringify({
success: true,
paymentIntentId: paymentIntent.id,
status: paymentIntent.status,
clientSecret: paymentIntent.client_secret,
}),
};
} catch (error: any) {
console.error('Payment processing error:', error);
return {
statusCode: 400,
headers: {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*',
},
body: JSON.stringify({
success: false,
error: error.message,
}),
};
}
};
This approach ensures PCI-DSS compliance by:
- Never storing raw card numbers in your systems
- Using tokenization provided by payment processors
- Implementing Strong Customer Authentication (SCA) required by European PSD2 regulations
- Logging payment events for audit trails without exposing sensitive data
Personalization with Amazon Personalize
Modern e-commerce platforms provide personalized product recommendations that increase conversion rates by 20-30%. Amazon Personalize uses the same machine learning technology that powers Amazon.com’s recommendations.
Implementation Example
import boto3
import json
personalize = boto3.client('personalize', region_name='eu-west-1')
personalize_runtime = boto3.client('personalize-runtime', region_name='eu-west-1')
personalize_events = boto3.client('personalize-events', region_name='eu-west-1')
# Track user interactions
def track_user_event(user_id, item_id, event_type='ProductView'):
"""Send interaction event to Personalize"""
event = {
'userId': user_id,
'itemId': item_id,
'eventType': event_type,
'sentAt': int(datetime.now().timestamp()),
}
personalize_events.put_events(
trackingId='your-tracking-id',
userId=user_id,
sessionId=f'session-{user_id}',
eventList=[event]
)
# Get recommendations
def get_product_recommendations(user_id, num_results=10):
"""Get personalized recommendations for user"""
response = personalize_runtime.get_recommendations(
campaignArn='arn:aws:personalize:eu-west-1:123456789012:campaign/ecommerce-campaign',
userId=user_id,
numResults=num_results,
)
recommended_product_ids = [item['itemId'] for item in response['itemList']]
return recommended_product_ids
# Get similar items
def get_similar_products(product_id, num_results=6):
"""Get products similar to given product"""
response = personalize_runtime.get_recommendations(
campaignArn='arn:aws:personalize:eu-west-1:123456789012:campaign/similar-items-campaign',
itemId=product_id,
numResults=num_results,
)
return [item['itemId'] for item in response['itemList']]
Real-World Architecture: Bol.com-Style Implementation
Let’s examine how to implement an architecture similar to Bol.com, the Netherlands’ largest online retailer:
Key Requirements
- Handle 10+ million visitors per month
- Process 50,000+ orders daily
- Support 30+ million product listings
- 99.9% uptime SLA
- Multi-vendor marketplace capabilities
Architecture Diagram
┌─────────────────────────────────────────────────────────┐
│ CloudFront CDN │
│ (Edge locations in Amsterdam, Frankfurt) │
└────────────┬─────────────────────────────┬──────────────┘
│ │
│ │
┌────────▼────────┐ ┌────────▼────────────┐
│ Static Assets │ │ Application LB │
│ (S3 + OAI) │ │ (Multi-AZ) │
└─────────────────┘ └─────────┬───────────┘
│
┌───────────────────┼───────────────────┐
│ │ │
┌────────▼────────┐ ┌───────▼────────┐ ┌───────▼────────┐
│ ECS Fargate │ │ ECS Fargate │ │ ECS Fargate │
│ (Web App) │ │ (API Layer) │ │ (Search) │
└────────┬────────┘ └───────┬────────┘ └───────┬────────┘
│ │ │
┌──────────────┴──────────────────┴─────────┬─────────┘
│ │
┌───────▼──────────┐ ┌─────────▼──────────┐
│ DynamoDB │ │ OpenSearch │
│ - Products │ │ - Product Search │
│ - Orders │ │ - Faceted Nav │
│ - Carts │ │ - Analytics │
└──────────────────┘ └────────────────────┘
│
┌───────▼──────────┐
│ ElastiCache │
│ (Redis) │
│ - Sessions │
│ - Hot Products │
└──────────────────┘
Best Practices for Production E-commerce
1. Monitoring and Observability
Implement comprehensive monitoring using CloudWatch and X-Ray:
import * as cloudwatch from 'aws-cdk-lib/aws-cloudwatch';
import * as sns from 'aws-cdk-lib/aws-sns';
import * as subscriptions from 'aws-cdk-lib/aws-sns-subscriptions';
import * as actions from 'aws-cdk-lib/aws-cloudwatch-actions';
// SNS topic for alerts
const alertTopic = new sns.Topic(this, 'AlertTopic');
alertTopic.addSubscription(new subscriptions.EmailSubscription('ops@forrict.nl'));
// Critical metric: API error rate
const apiErrorAlarm = new cloudwatch.Alarm(this, 'ApiErrorAlarm', {
metric: api.metricServerError(),
threshold: 10,
evaluationPeriods: 2,
datapointsToAlarm: 2,
treatMissingData: cloudwatch.TreatMissingData.NOT_BREACHING,
alarmDescription: 'API error rate is too high',
});
apiErrorAlarm.addAlarmAction(new actions.SnsAction(alertTopic));
// Cart abandonment tracking
const cartAbandonmentMetric = new cloudwatch.Metric({
namespace: 'Ecommerce',
metricName: 'CartAbandonment',
statistic: 'Average',
});
// CloudFront cache hit ratio
const cacheHitAlarm = new cloudwatch.Alarm(this, 'CacheHitAlarm', {
metric: distribution.metricCacheHitRate(),
threshold: 85,
comparisonOperator: cloudwatch.ComparisonOperator.LESS_THAN_THRESHOLD,
evaluationPeriods: 3,
alarmDescription: 'CloudFront cache hit rate is below 85%',
});
2. Cost Optimization
Implement cost controls for variable traffic:
- Use Savings Plans for baseline ECS/Lambda capacity
- Enable DynamoDB Auto Scaling to reduce costs during low traffic
- Use S3 Intelligent-Tiering for product images
- Implement CloudFront reserved capacity for predictable traffic
- Right-size ElastiCache instances based on actual memory usage
3. Security Hardening
Essential security measures:
- Enable AWS WAF on CloudFront to block common attacks
- Implement AWS Shield Standard (free) or Advanced for DDoS protection
- Use AWS Secrets Manager for API keys and database credentials
- Enable VPC Flow Logs for network monitoring
- Implement GuardDuty for threat detection
- Enable MFA for all AWS accounts
- Use IAM roles with least-privilege permissions
4. Disaster Recovery
Implement multi-region failover:
// DynamoDB Global Tables for multi-region replication
const globalTable = new dynamodb.Table(this, 'GlobalProductTable', {
partitionKey: { name: 'productId', type: dynamodb.AttributeType.STRING },
billingMode: dynamodb.BillingMode.PAY_PER_REQUEST,
replicationRegions: ['eu-central-1', 'us-east-1'],
pointInTimeRecovery: true,
stream: dynamodb.StreamViewType.NEW_AND_OLD_IMAGES,
});
Conclusion
Building a scalable e-commerce platform on AWS requires careful architecture decisions and deep understanding of traffic patterns, especially in the Dutch market with its unique shopping behaviors. By leveraging CloudFront for global content delivery, DynamoDB for flexible product catalogs, Auto Scaling for traffic spikes, and managed services for payments and personalization, you can build a platform that rivals major Dutch retailers like Bol.com and Coolblue.
The key advantages of AWS for e-commerce are:
Elasticity: Automatically scale from hundreds to millions of users without manual intervention or over-provisioning.
Global Reach: Deliver content with low latency to customers across Europe and worldwide using CloudFront’s edge network.
Managed Services: Focus on business logic rather than infrastructure management with services like DynamoDB, Lambda, and Personalize.
Security and Compliance: Built-in tools for PCI-DSS compliance, GDPR data protection, and DDoS mitigation.
Cost Efficiency: Pay only for what you use, with automatic scaling down during low-traffic periods.
At Forrict, we help Dutch e-commerce companies architect, build, and optimize their AWS infrastructure. Whether you’re launching a new online store or scaling an existing platform, our team brings deep expertise in AWS services and e-commerce best practices. Contact us to discuss how we can help you build a world-class e-commerce platform on AWS.
Forrict Team
AWS expert and consultant at Forrict, specializing in cloud architecture and AWS best practices for Dutch businesses.