AWS voor Retail en E-commerce: Schaalbare Online Winkels Bouwen | Forrict Ga naar hoofdinhoud
Cloud Architectuur E-commerce AWS Services

AWS voor Retail en E-commerce: Schaalbare Online Winkels Bouwen

Forrict Team
AWS voor Retail en E-commerce: Schaalbare Online Winkels Bouwen
Uitgebreide gids voor het bouwen van schaalbare e-commerce platforms op AWS met DynamoDB, CloudFront en Auto Scaling voor piekverkeer.

De Nederlandse e-commerce markt groeit explosief, met online retailverkopen die de €30 miljard per jaar overschrijden. Grote spelers zoals Bol.com en Coolblue hebben de standaard gezet voor klantervaring, wat infrastructuur vereist die miljoenen bezoekers kan verwerken tijdens piekevents zoals Black Friday, Sinterklaas en Cyber Monday. Amazon Web Services (AWS) biedt de perfecte basis voor het bouwen van schaalbare, veilige en performante e-commerce platforms.

Waarom AWS voor E-commerce?

Het e-commerce landschap vraagt om infrastructuur die kan schalen van honderden naar miljoenen gelijktijdige gebruikers binnen enkele minuten. Traditionele hosting oplossingen hebben moeite met deze elasticiteit, wat leidt tot website crashes tijdens piekperiodes, omzetverlies en beschadigde merkreputatie. AWS biedt een uitgebreide suite van services specifiek ontworpen voor e-commerce workloads:

Schaalbaarheid: Auto Scaling groepen kunnen automatisch compute capaciteit toevoegen of verwijderen op basis van verkeerspatronen, zodat uw site responsief blijft tijdens onverwachte verkeerspieken.

Wereldwijde Levering: CloudFront’s edge locations door heel Europa en wereldwijd leveren content met milliseconde latency, cruciaal voor het converteren van internationale klanten.

Beveiliging en Compliance: AWS services voldoen aan PCI-DSS eisen voor betalingsverwerking, AVG voor Europese klantdata, en bieden ingebouwde DDoS bescherming via AWS Shield.

Kostenoptimalisatie: Pay-per-use pricing betekent dat u alleen betaalt voor de resources die u verbruikt, zonder over-provisioning voor piekcapaciteit die het grootste deel van het jaar ongebruikt blijft.

Toonaangevende Nederlandse retailers hebben bewezen dat deze aanpak werkt. Toen Coolblue uitbreidde naar België en Duitsland, maakten ze gebruik van AWS’s multi-region mogelijkheden om consistente prestaties over grenzen heen te behouden. Evenzo hebben kleinere Nederlandse modewinkels en speciaalzaken AWS gebruikt om te concurreren met grotere spelers door enterprise-grade klantervaringen te leveren zonder enterprise-scale infrastructuurkosten.

E-commerce Architectuur op AWS

Een modern e-commerce platform bestaat uit verschillende belangrijke componenten: productcatalogus management, winkelwagen en sessie handling, orderverwerking, betalingsintegratie, content delivery en analytics. Laten we een referentie architectuur verkennen die deze vereisten efficiënt afhandelt.

Kern Architectuur Componenten

De basis van een schaalbaar e-commerce platform op AWS omvat typisch:

Frontend Delivery: Statische website assets (HTML, CSS, JavaScript) gehost in Amazon S3 en wereldwijd gedistribueerd via CloudFront CDN. Deze aanpak vermindert hostingkosten drastisch terwijl prestaties verbeteren.

Applicatie Laag: API Gateway biedt het toegangspunt voor alle backend operaties, en routeert verzoeken naar Lambda functies (serverless) of gecontaineriseerde applicaties die draaien op ECS/EKS (voor complexere bedrijfslogica).

Data Laag: DynamoDB voor productcatalogus, winkelwagens en gebruikerssessies; RDS voor transactionele orderdata; ElastiCache voor veelvuldig opgevraagde data zoals featured producten en prijzen.

Zoeken en Ontdekken: Amazon OpenSearch Service ondersteunt productzoekopdrachten, gefacetteerde navigatie en aanbevelingen, en biedt de snelle, relevante resultaten die klanten verwachten.

Betalingsverwerking: Integratie met betalingsproviders via veilige Lambda functies, waarbij gevoelige data nooit uw applicatieservers raakt.

Hier is een compleet AWS CDK voorbeeld dat de kern infrastructuur implementeert:

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 voor ElastiCache en private resources
    const vpc = new ec2.Vpc(this, 'EcommerceVPC', {
      maxAzs: 2,
      natGateways: 1,
    });

    // S3 bucket voor statische website assets
    const websiteBucket = new s3.Bucket(this, 'WebsiteBucket', {
      websiteIndexDocument: 'index.html',
      publicReadAccess: false,
      blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL,
      removalPolicy: cdk.RemovalPolicy.RETAIN,
    });

    // CloudFront distributie met caching optimalisaties
    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, // Europa en Noord-Amerika
    });

    // DynamoDB tabel voor productcatalogus
    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 voor categorie-gebaseerde queries
    productTable.addGlobalSecondaryIndex({
      indexName: 'CategoryIndex',
      partitionKey: { name: 'category', type: dynamodb.AttributeType.STRING },
      sortKey: { name: 'price', type: dynamodb.AttributeType.NUMBER },
    });

    // DynamoDB tabel voor winkelwagens
    const cartTable = new dynamodb.Table(this, 'CartTable', {
      partitionKey: { name: 'userId', type: dynamodb.AttributeType.STRING },
      timeToLiveAttribute: 'expiryTime',
      billingMode: dynamodb.BillingMode.PAY_PER_REQUEST,
    });

    // DynamoDB tabel voor bestellingen
    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 voor sessie en product caching
    const subnetGroup = new elasticache.CfnSubnetGroup(this, 'CacheSubnetGroup', {
      description: 'Subnet groep voor Redis cache',
      subnetIds: vpc.privateSubnets.map(subnet => subnet.subnetId),
    });

    const securityGroup = new ec2.SecurityGroup(this, 'CacheSecurityGroup', {
      vpc,
      description: 'Security group voor Redis cache',
      allowAllOutbound: true,
    });

    const redisCache = new elasticache.CfnReplicationGroup(this, 'RedisCache', {
      replicationGroupDescription: 'Redis cache voor e-commerce',
      engine: 'redis',
      cacheNodeType: 'cache.r6g.large',
      numCacheClusters: 2,
      automaticFailoverEnabled: true,
      cacheSubnetGroupName: subnetGroup.ref,
      securityGroupIds: [securityGroup.securityGroupId],
      multiAzEnabled: true,
    });

    // Lambda functie voor 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),
      'Lambda toegang tot Redis toestaan'
    );

    // Lambda functie voor winkelwagen operaties
    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 functie voor orderverwerking
    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 voor e-commerce operaties',
      deployOptions: {
        throttlingRateLimit: 1000,
        throttlingBurstLimit: 2000,
        metricsEnabled: true,
        loggingLevel: apigateway.MethodLoggingLevel.INFO,
      },
    });

    // API resources en 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 voor 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 distributie domein',
    });

    new cdk.CfnOutput(this, 'ApiEndpoint', {
      value: api.url,
      description: 'API Gateway endpoint',
    });

    new cdk.CfnOutput(this, 'RedisEndpoint', {
      value: redisCache.attrPrimaryEndPointAddress,
      description: 'Redis cache endpoint',
    });
  }
}

Deze CDK stack creëert een productie-klare e-commerce infrastructuur met wereldwijde content delivery, serverless API endpoints en een zeer schaalbare data laag.

Auto Scaling voor Piekverkeer Events

Nederlandse consumenten zijn zeer betrokken bij seizoensgebonden winkelevents. Black Friday ziet verkeerspieken van 300-500% vergeleken met normale dagen, terwijl Sinterklaas (begin december) wekenlang aanhoudend hoog verkeer genereert. Uw infrastructuur moet deze patronen aankunnen zonder handmatige interventie.

Verkeerspatroon Analyse

Voordat u Auto Scaling implementeert, moet u uw verkeerskenmerken begrijpen:

Voorspelbare Pieken: Events zoals Black Friday, Sinterklaas en zomerverkoop gebeuren op bekende data. Gebruik geplande scaling om capaciteit vooraf op te warmen.

Onvoorspelbare Stijgingen: Virale social media posts, influencer vermeldingen of nieuwsberichtgeving kunnen plotseling verkeer genereren. Reactieve scaling handelt deze scenario’s af.

Regionale Variaties: Nederlandse winkelpatronen verschillen van andere markten. Sinterklaas is uniek belangrijk in Nederland en België, terwijl Koningsdag verkopen specifiek Nederlands zijn.

Implementatie van Elastic Load Balancing en Auto Scaling

Voor gecontaineriseerde e-commerce applicaties biedt ECS met Auto Scaling de juiste balans tussen controle en automatisering:

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 certificaat
});

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 gebaseerd op CPU en 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),
});

// Geplande scaling voor Black Friday (laatste vrijdag van 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,
});

// Geplande scaling voor Sinterklaas periode (1-6 december)
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,
});

Deze configuratie zorgt ervoor dat uw applicatie prestaties behoudt tijdens zowel voorspelbare events (Black Friday, Sinterklaas) als onverwachte verkeerspieken.

CloudFront CDN voor Wereldwijde Levering

Content Delivery Networks zijn essentieel voor e-commerce prestaties. Studies tonen aan dat 53% van mobiele gebruikers sites verlaat die langer dan 3 seconden laden, en een vertraging van 100ms kan conversies met 7% verminderen. CloudFront levert content vanaf edge locations het dichtst bij uw gebruikers, wat latency drastisch vermindert.

CloudFront Architectuur voor E-commerce

Een effectieve CloudFront setup voor e-commerce omvat meerdere cache behaviors:

Statische Assets: Afbeeldingen, CSS, JavaScript bestanden gecached voor dagen of weken met agressieve compressie.

Productpagina’s: Dynamische content met kortere cache tijden (5-15 minuten) die updates wanneer producten veranderen.

Gepersonaliseerde Content: Gebruikersspecifieke content zoals winkelwagens die caching volledig omzeilt.

API Verzoeken: Gerouteerd naar uw backend met connection pooling en keep-alive voor efficiëntie.

Hier is een geavanceerde CloudFront configuratie geoptimaliseerd voor Nederlandse en Europese klanten:

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 voor veilige S3 toegang
const originAccessIdentity = new cloudfront.OriginAccessIdentity(this, 'OAI');
websiteBucket.grantRead(originAccessIdentity);

// Custom cache policy voor productpagina's
const productCachePolicy = new cloudfront.CachePolicy(this, 'ProductCachePolicy', {
  cachePolicyName: 'ProductPageCache',
  comment: 'Cache policy voor productpaginas',
  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', 'kleur', 'maat'),
  headerBehavior: cloudfront.CacheHeaderBehavior.allowList(
    'CloudFront-Viewer-Country',
    'CloudFront-Viewer-Currency',
    'Accept-Language'
  ),
  enableAcceptEncodingGzip: true,
  enableAcceptEncodingBrotli: true,
});

// Response headers policy voor beveiliging en prestaties
const responseHeadersPolicy = new cloudfront.ResponseHeadersPolicy(this, 'SecurityHeaders', {
  comment: 'Security headers voor 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 },
    ],
  },
});

// Hoofd distributie
const distribution = new cloudfront.Distribution(this, 'EcommerceDistribution', {
  comment: 'E-commerce website distributie',
  priceClass: cloudfront.PriceClass.PRICE_CLASS_100, // Europa en Noord-Amerika
  geoRestriction: cloudfront.GeoRestriction.allowlist('NL', 'BE', 'DE', 'FR', 'GB', 'US'),

  // Default behavior voor statische assets
  defaultBehavior: {
    origin: new origins.S3Origin(websiteBucket, {
      originAccessIdentity: originAccessIdentity,
    }),
    viewerProtocolPolicy: cloudfront.ViewerProtocolPolicy.REDIRECT_TO_HTTPS,
    cachePolicy: cloudfront.CachePolicy.CACHING_OPTIMIZED,
    compress: true,
    responseHeadersPolicy: responseHeadersPolicy,
  },

  // Aanvullende behaviors
  additionalBehaviors: {
    // Product afbeeldingen met lange 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,
    },

    // Productpagina's met 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,
    },

    // Winkelwagen - geen 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 - geen 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/',
});

Deze CloudFront configuratie optimaliseert caching strategieën op basis van content type terwijl beveiliging en prestaties behouden blijven.

Productcatalogus met DynamoDB

DynamoDB is ideaal voor productcatalogi vanwege het vermogen om te schalen naar miljoenen items, onvoorspelbare verkeerspieken af te handelen en een latency van enkele milliseconden te bieden. In tegenstelling tot traditionele relationele databases, staat DynamoDB’s NoSQL ontwerp flexibele schema’s toe die variërende productattributen accommoderen.

Schema Ontwerp voor Productcatalogus

Effectief DynamoDB schema ontwerp vereist eerst nadenken over toegangspatronen:

# Productcatalogus schema ontwerp
import boto3
from decimal import Decimal
from datetime import datetime

dynamodb = boto3.resource('dynamodb', region_name='eu-west-1')
product_table = dynamodb.Table('Products')

# Voorbeeld product item
product_item = {
    'productId': 'PROD-12345',  # Partition key
    'category': 'Elektronica',
    'subcategory': 'Laptops',
    'brand': 'Dell',
    'name': 'Dell XPS 15',
    'description': 'Premium laptop met 15-inch display',
    'price': Decimal('1299.99'),
    'currency': 'EUR',
    'stock': 45,
    'warehouse': 'NL-AMSTERDAM',
    'attributes': {
        'processor': 'Intel i7-12700H',
        'ram': '16GB',
        'opslag': '512GB SSD',
        'display': '15.6-inch FHD',
        'gewicht': '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', 'zakelijk'],
    'inStock': True,
    'createdAt': int(datetime.now().timestamp()),
    'updatedAt': int(datetime.now().timestamp()),
    'SEO': {
        'title': 'Dell XPS 15 Laptop - Premium Prestaties',
        'description': 'Koop Dell XPS 15 met Intel i7, 16GB RAM, 512GB SSD',
        'keywords': ['Dell XPS', 'laptop', 'premium laptop'],
    },
}

# Product invoegen
product_table.put_item(Item=product_item)

# Producten opvragen per categorie (met GSI)
response = product_table.query(
    IndexName='CategoryIndex',
    KeyConditionExpression='category = :cat',
    ExpressionAttributeValues={
        ':cat': 'Elektronica'
    }
)

# Query met prijs filtering
response = product_table.query(
    IndexName='CategoryIndex',
    KeyConditionExpression='category = :cat AND price BETWEEN :min AND :max',
    ExpressionAttributeValues={
        ':cat': 'Elektronica',
        ':min': Decimal('1000'),
        ':max': Decimal('1500'),
    }
)

Implementatie van Product Zoeken met Caching

Combineer DynamoDB met ElastiCache Redis voor optimale prestaties:

import boto3
import redis
import json
from decimal import Decimal

# Initialiseer 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):
    """Product ophalen met Redis caching"""
    cache_key = f'product:{product_id}'

    # Probeer eerst cache
    cached = redis_client.get(cache_key)
    if cached:
        print(f'Cache HIT voor {product_id}')
        return json.loads(cached)

    # Cache miss - ophalen uit DynamoDB
    print(f'Cache MISS voor {product_id}')
    response = product_table.get_item(Key={'productId': product_id})

    if 'Item' not in response:
        return None

    product = response['Item']

    # Opslaan in cache voor 1 uur
    redis_client.setex(
        cache_key,
        3600,
        json.dumps(product, cls=DecimalEncoder)
    )

    return product

def get_featured_products(category, limit=12):
    """Featured producten ophalen met list caching"""
    cache_key = f'featured:{category}:{limit}'

    # Probeer cache
    cached = redis_client.get(cache_key)
    if cached:
        return json.loads(cached)

    # Ophalen uit 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,  # Aflopende volgorde
    )

    products = response['Items']

    # Cache voor 15 minuten
    redis_client.setex(
        cache_key,
        900,
        json.dumps(products, cls=DecimalEncoder)
    )

    return products

def update_product_price(product_id, new_price):
    """Prijs updaten en cache invalideren"""
    # Updaten 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()),
        }
    )

    # Cache invalideren
    redis_client.delete(f'product:{product_id}')

    # Ook categorie caches invalideren
    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'Prijs geüpdatet voor {product_id} en caches geïnvalideerd')

Deze implementatie biedt milliseconde responstijden voor veelvuldig opgevraagde producten terwijl data consistentie gegarandeerd wordt wanneer prijzen of voorraad verandert.

Betalingsverwerking en PCI-DSS Compliance

Betalingsbeveiliging is van het grootste belang voor e-commerce. De Payment Card Industry Data Security Standard (PCI-DSS) vereist strikte controles rondom kaarthoudergegevens. AWS biedt tools om PCI-DSS compliant betalingsflows te bouwen zonder gevoelige kaartgegevens op te slaan in uw systemen.

Tokenisatie Architectuur

Moderne e-commerce platforms gebruiken betaling tokenisatie, waarbij gevoelige kaartgegevens nooit uw servers raken:

// Lambda functie voor betalingsverwerking met 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!);

    // Payment intent aanmaken
    const paymentIntent = await stripe.paymentIntents.create({
      amount: Math.round(amount * 100), // Omzetten naar centen
      currency: currency || 'eur',
      payment_method: paymentMethodId,
      customer: customerId,
      confirm: true,
      metadata: {
        orderId: orderId,
        source: 'ecommerce-website',
      },
      // 3D Secure authenticatie vereisen voor EU regelgeving
      payment_method_options: {
        card: {
          request_three_d_secure: 'automatic',
        },
      },
    });

    // Order updaten met betaalstatus
    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();

    // Client secret retourneren voor 3DS bevestiging indien nodig
    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('Betalingsverwerkingsfout:', error);

    return {
      statusCode: 400,
      headers: {
        'Content-Type': 'application/json',
        'Access-Control-Allow-Origin': '*',
      },
      body: JSON.stringify({
        success: false,
        error: error.message,
      }),
    };
  }
};

Deze aanpak garandeert PCI-DSS compliance door:

  • Nooit ruwe kaartnummers op te slaan in uw systemen
  • Tokenisatie te gebruiken die door betalingsprocessors wordt aangeboden
  • Strong Customer Authentication (SCA) te implementeren zoals vereist door Europese PSD2 regelgeving
  • Betalingsevents te loggen voor audit trails zonder gevoelige data bloot te leggen

Personalisatie met Amazon Personalize

Moderne e-commerce platforms bieden gepersonaliseerde productaanbevelingen die conversieratio’s met 20-30% verhogen. Amazon Personalize gebruikt dezelfde machine learning technologie die Amazon.com’s aanbevelingen aandrijft.

Implementatie Voorbeeld

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')

# Gebruikersinteracties tracken
def track_user_event(user_id, item_id, event_type='ProductView'):
    """Interactie event naar Personalize sturen"""
    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]
    )

# Aanbevelingen ophalen
def get_product_recommendations(user_id, num_results=10):
    """Gepersonaliseerde aanbevelingen voor gebruiker ophalen"""
    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

# Vergelijkbare items ophalen
def get_similar_products(product_id, num_results=6):
    """Producten ophalen die vergelijkbaar zijn met gegeven 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 Architectuur: Bol.com-Stijl Implementatie

Laten we onderzoeken hoe een architectuur vergelijkbaar met Bol.com te implementeren, de grootste online retailer van Nederland:

Belangrijkste Vereisten

  • 10+ miljoen bezoekers per maand verwerken
  • 50.000+ bestellingen per dag verwerken
  • 30+ miljoen productlijsten ondersteunen
  • 99.9% uptime SLA
  • Multi-vendor marketplace mogelijkheden

Architectuur Diagram

┌─────────────────────────────────────────────────────────┐
│                    CloudFront CDN                        │
│         (Edge locations in Amsterdam, Frankfurt)         │
└────────────┬─────────────────────────────┬──────────────┘
             │                             │
             │                             │
    ┌────────▼────────┐          ┌────────▼────────────┐
    │   Statische     │          │   Application LB    │
    │   Assets        │          │   (Multi-AZ)        │
    │   (S3 + OAI)    │          └─────────┬───────────┘
    └─────────────────┘                    │
                       ┌───────────────────┼───────────────────┐
                       │                   │                   │
              ┌────────▼────────┐ ┌───────▼────────┐ ┌───────▼────────┐
              │   ECS Fargate   │ │  ECS Fargate   │ │  ECS Fargate   │
              │   (Web App)     │ │  (API Layer)   │ │  (Search)      │
              └────────┬────────┘ └───────┬────────┘ └───────┬────────┘
                       │                  │                   │
        ┌──────────────┴──────────────────┴─────────┬─────────┘
        │                                            │
┌───────▼──────────┐                      ┌─────────▼──────────┐
│   DynamoDB       │                      │  OpenSearch        │
│   - Producten    │                      │  - Product Zoeken  │
│   - Bestellingen │                      │  - Faceted Nav     │
│   - Winkelwagens │                      │  - Analytics       │
└──────────────────┘                      └────────────────────┘

┌───────▼──────────┐
│  ElastiCache     │
│  (Redis)         │
│  - Sessies       │
│  - Hot Products  │
└──────────────────┘

Best Practices voor Productie E-commerce

1. Monitoring en Observability

Implementeer uitgebreide monitoring met CloudWatch en 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 voor alerts
const alertTopic = new sns.Topic(this, 'AlertTopic');
alertTopic.addSubscription(new subscriptions.EmailSubscription('ops@forrict.nl'));

// Kritieke 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 te hoog',
});
apiErrorAlarm.addAlarmAction(new actions.SnsAction(alertTopic));

// Winkelwagen 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 onder 85%',
});

2. Kostenoptimalisatie

Implementeer kostcontroles voor variabel verkeer:

  • Gebruik Savings Plans voor baseline ECS/Lambda capaciteit
  • Schakel DynamoDB Auto Scaling in om kosten te verlagen tijdens laag verkeer
  • Gebruik S3 Intelligent-Tiering voor productafbeeldingen
  • Implementeer CloudFront reserved capacity voor voorspelbaar verkeer
  • Right-size ElastiCache instances op basis van daadwerkelijk geheugengebruik

3. Beveiligingsharding

Essentiële beveiligingsmaatregelen:

  • Schakel AWS WAF in op CloudFront om veelvoorkomende aanvallen te blokkeren
  • Implementeer AWS Shield Standard (gratis) of Advanced voor DDoS bescherming
  • Gebruik AWS Secrets Manager voor API keys en database credentials
  • Schakel VPC Flow Logs in voor netwerkmonitoring
  • Implementeer GuardDuty voor threat detection
  • Schakel MFA in voor alle AWS accounts
  • Gebruik IAM roles met least-privilege permissions

4. Disaster Recovery

Implementeer multi-region failover:

// DynamoDB Global Tables voor multi-region replicatie
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,
});

Conclusie

Het bouwen van een schaalbaar e-commerce platform op AWS vereist zorgvuldige architectuurbeslissingen en diep begrip van verkeerspatronen, vooral in de Nederlandse markt met zijn unieke winkelgedrag. Door CloudFront in te zetten voor wereldwijde content delivery, DynamoDB voor flexibele productcatalogi, Auto Scaling voor verkeerspieken en managed services voor betalingen en personalisatie, kunt u een platform bouwen dat rivaliseren kan met grote Nederlandse retailers zoals Bol.com en Coolblue.

De belangrijkste voordelen van AWS voor e-commerce zijn:

Elasticiteit: Automatisch schalen van honderden naar miljoenen gebruikers zonder handmatige interventie of over-provisioning.

Wereldwijd Bereik: Content leveren met lage latency aan klanten door heel Europa en wereldwijd via CloudFront’s edge netwerk.

Managed Services: Focus op bedrijfslogica in plaats van infrastructuur management met services zoals DynamoDB, Lambda en Personalize.

Beveiliging en Compliance: Ingebouwde tools voor PCI-DSS compliance, AVG databescherming en DDoS mitigatie.

Kostenefficiëntie: Betaal alleen voor wat u gebruikt, met automatisch afschalen tijdens periodes met weinig verkeer.

Bij Forrict helpen we Nederlandse e-commerce bedrijven hun AWS infrastructuur te architecturen, bouwen en optimaliseren. Of u nu een nieuwe online winkel lanceert of een bestaand platform schaalt, ons team brengt diepe expertise in AWS services en e-commerce best practices. Neem contact met ons op om te bespreken hoe we u kunnen helpen een e-commerce platform van wereldklasse op AWS te bouwen.

F

Forrict Team

AWS expert en consultant bij Forrict, gespecialiseerd in cloud architectuur en AWS best practices voor Nederlandse bedrijven.

Tags

AWS E-commerce Retail DynamoDB CloudFront Auto Scaling Nederland Architectuur

Gerelateerde Artikelen

Klaar om je AWS Infrastructuur te Transformeren?

Laten we bespreken hoe we je cloud journey kunnen optimaliseren