Skip to main content
โšก Calmops

Cloud Architecture Patterns: Modern Design Principles for 2025

Introduction

Building effective cloud applications requires understanding proven architectural patterns. In 2025, these patterns have evolved to address modern challenges: extreme scalability, global distribution, cost optimization, and resilience. This guide covers essential cloud architecture patterns with practical examples.


Foundational Patterns

1. N-Tier Architecture

The classic multi-tier pattern adapted for cloud:

# Three-tier cloud architecture
tiers:
  - name: "Presentation Tier"
    components: ["CloudFront", "ALB", "API Gateway"]
    scaling: "Auto-scaling based on request count"
    
  - name: "Application Tier"
    components: ["ECS Fargate", "Lambda", "App Runner"]
    scaling: "CPU/memory based"
    
  - name: "Data Tier"
    components: ["RDS", "ElastiCache", "S3"]
    scaling: "Read replicas, sharding"
# Example: Three-tier infrastructure
# Presentation Tier
resource "aws_lb" "app" {
  name               = "app-lb"
  internal           = false
  load_balancer_type = "application"
  security_groups    = [aws_security_group.alb.id]
  subnets           = aws_subnet.public[*].id
}

# Application Tier
resource "aws_ecs_service" "app" {
  name            = "app-service"
  cluster         = aws_ecs_cluster.main.id
  task_definition = aws_ecs_task_definition.app.arn
  desired_count   = 3
  
  load_balancer {
    target_group_arn = aws_lb_target_group.app.arn
    container_port   = 8080
  }
  
  deployment_controller {
    type = "CODE_DEPLOY"
  }
}

# Data Tier
resource "aws_db_instance" "main" {
  identifier     = "main-db"
  engine         = "postgres"
  instance_class = "db.t3.medium"
  multi_az       = true
}

2. Queue-Based Processing

Decouple components with message queues:

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”     โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”     โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚   Producer  โ”‚โ”€โ”€โ”€โ”€โ–ถโ”‚    Queue    โ”‚โ”€โ”€โ”€โ”€โ–ถโ”‚   Consumer  โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜     โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜     โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                           โ”‚
                    โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”€โ”€โ”
                    โ”‚   DLQ       โ”‚
                    โ”‚ (Failed)    โ”‚
                    โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
# AWS SQS Producer
import boto3
import json

sqs = boto3.client('sqs')

def send_order(order_data):
    """Send order to processing queue"""
    response = sqs.send_message(
        QueueUrl='https://sqs.us-east-1.amazonaws.com/123456789/orders',
        MessageBody=json.dumps(order_data),
        MessageAttributes={
            'OrderType': {
                'StringValue': order_data['type'],
                'DataType': 'String'
            },
            'Priority': {
                'StringValue': 'normal',
                'DataType': 'String'
            }
        },
        DelaySeconds=0
    )
    return response['MessageId']
# AWS SQS Consumer with Lambda
import json
import boto3

sqs = boto3.resource('sqs')

def process_order(event, context):
    """Lambda handler for SQS messages"""
    for record in event['Records']:
        order = json.loads(record['body'])
        
        try:
            # Process order
            result = process(order)
            
            # Success metric
            cloudwatch.put_metric_data(
                Namespace='Orders',
                MetricData=[{
                    'MetricName': 'Processed',
                    'Value': 1,
                    'Unit': 'Count'
                }]
            )
            
        except Exception as e:
            # Send to DLQ for failed processing
            dlq = sqs.Queue('https://sqs.us-east-1.amazonaws.com/123456789/orders-dlq')
            dlq.send_message(MessageBody=json.dumps(order))
            raise

Microservices Patterns

Service Decomposition

# Example: E-commerce microservices
services:
  - name: "product-service"
    responsibility: "Product catalog, inventory"
    database: "DynamoDB"
    api: "/api/products"
    
  - name: "order-service"
    responsibility: "Order processing"
    database: "PostgreSQL (RDS)"
    api: "/api/orders"
    
  - name: "payment-service"
    responsibility: "Payment processing"
    database: "Dedicated SQL"
    api: "/api/payments"
    compliance: "PCI-DSS"
    
  - name: "shipping-service"
    responsibility: "Shipping calculations"
    database: "MongoDB"
    api: "/api/shipping"
    
  - name: "notification-service"
    responsibility: "Email, SMS, push"
    pattern: "Event-driven"

API Gateway Pattern

# API Gateway configuration (AWS)
api_gateway:
  name: "ecommerce-api"
  
  routes:
    - path: "/products/*"
      integration: "HTTP: product-service:8080"
      auth: "Cognito"
      
    - path: "/orders/*"
      integration: "HTTP: order-service:8080"
      auth: "Cognito"
      rate_limit: "1000/hour"
      
    - path: "/payments/*"
      integration: "HTTP: payment-service:8080"
      auth: "Cognito"
      rate_limit: "100/hour"
      
    - path: "/notifications/*"
      integration: "HTTP: notification-service:8080"
      auth: "Cognito"
// Custom authorizer example
exports.handler = async (event) => {
  const token = event.authorizationToken;
  
  try {
    // Verify JWT
    const payload = jwt.verify(token, process.env.JWT_SECRET);
    
    return {
      principalId: payload.sub,
      policyDocument: {
        Version: '2012-10-17',
        Statement: [{
          Action: 'execute-api:Invoke',
          Effect: 'Allow',
          Resource: event.methodArn
        }]
      },
      context: {
        userId: payload.sub,
        role: payload.role
      }
    };
  } catch (e) {
    return {
      principalId: 'unauthorized',
      policyDocument: {
        Version: '2012-10-17',
        Statement: [{
          Action: 'execute-api:Invoke',
          Effect: 'Deny',
          Resource: event.methodArn
        }]
      }
    };
  }
};

Service Mesh

# Istio service mesh configuration
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: product-service
spec:
  hosts:
    - product-service
  http:
    - match:
        - headers:
            x-canary:
              exact: "true"
      route:
        - destination:
            host: product-service
            subset: v2
          weight: 20
        - destination:
            host: product-service
            subset: v1
          weight: 80
          
---
# Traffic splitting for canary releases
apiVersion: networking.istio.io/v1beta1
kind: DestinationRule
metadata:
  name: product-service
spec:
  host: product-service
  subsets:
    - name: v1
      labels:
        version: "1.0.0"
    - name: v2
      labels:
        version: "2.0.0"

Event-Driven Architecture

Event Streaming with Kafka

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                     Event Flow                              โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚                                                             โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”    โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”    โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”    โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚
โ”‚  โ”‚ Order   โ”‚โ”€โ”€โ”€โ–ถโ”‚  Kafka   โ”‚โ”€โ”€โ”€โ–ถโ”‚ Inventoryโ”‚โ”€โ”€โ”€โ–ถโ”‚ Shippingโ”‚ โ”‚
โ”‚  โ”‚ Service โ”‚    โ”‚  Topic   โ”‚    โ”‚ Service  โ”‚    โ”‚ Service โ”‚ โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜    โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜    โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜    โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚
โ”‚       โ”‚              โ”‚                                    โ”‚
โ”‚       โ”‚              โ–ผ                                    โ”‚
โ”‚       โ”‚         โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”                               โ”‚
โ”‚       โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ถโ”‚Analyticsโ”‚                               โ”‚
โ”‚                 โ”‚ Service โ”‚                               โ”‚
โ”‚                 โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜                               โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
# Kafka Producer: Order Service
from kafka import KafkaProducer
import json

producer = KafkaProducer(
    bootstrap_servers=['kafka:9092'],
    value_serializer=lambda v: json.dumps(v).encode('utf-8')
)

def create_order(order_data):
    """Create order and publish event"""
    order = {
        'order_id': generate_id(),
        'customer_id': order_data['customer_id'],
        'items': order_data['items'],
        'total': calculate_total(order_data),
        'timestamp': datetime.utcnow().isoformat()
    }
    
    # Save to database
    db.orders.insert(order)
    
    # Publish event
    producer.send('orders.created', order)
    producer.flush()
    
    return order['order_id']
# Kafka Consumer: Inventory Service
from kafka import KafkaConsumer
import json

consumer = KafkaConsumer(
    'orders.created',
    bootstrap_servers=['kafka:9092'],
    value_deserializer=lambda v: json.loads(v.decode('utf-8')),
    group_id='inventory-service',
    auto_offset_reset='earliest'
)

def update_inventory(order):
    """Process order and update inventory"""
    for item in order['items']:
        # Decrement inventory
        result = db.inventory.update_one(
            {'product_id': item['product_id']},
            {'$inc': {'quantity': -item['quantity']}}
        )
        
        if result.modified_count == 0:
            # Publish out-of-stock event
            producer.send('inventory.insufficient', {
                'order_id': order['order_id'],
                'product_id': item['product_id']
            })

for message in consumer:
    update_inventory(message.value)

EventBridge/SNS Patterns

# AWS EventBridge event bus
EventBus:
  name: "enterprise-events"
  
  Rules:
    - name: "order-processor"
      event_pattern:
        source:
          - "orders.service"
        detail_type:
          - "OrderCreated"
          - "OrderUpdated"
      targets:
        - arn: "lambda:process-order"
          
    - name: "analytics-pipeline"
      event_pattern:
        source:
          - "orders.service"
          - "payments.service"
      targets:
        - arn: "kinesis:analytics-stream"

Serverless Patterns

Function as a Service

# Serverless application architecture
serverless:
  api:
    provider: "AWS Lambda"
    runtime: "nodejs18"
    memory: 512MB
    timeout: 30s
    
  storage:
    provider: "S3"
    
  database:
    provider: "DynamoDB"
    
  queue:
    provider: "SQS"
    
  auth:
    provider: "Cognito"
// Lambda handler: Image processing
const AWS = require('aws-sdk');
const sharp = require('sharp');

const s3 = new AWS.S3();

exports.handler = async (event) => {
    // Get image from S3
    const bucket = event.Records[0].s3.bucket.name;
    const key = decodeURIComponent(event.Records[0].s3.object.key);
    
    const image = await s3.getObject({
        Bucket: bucket,
        Key: key
    }).promise();
    
    // Process image
    const sizes = [640, 1280, 1920];
    const promises = sizes.map(async (width) => {
        const buffer = await sharp(image.Body)
            .resize(width)
            .jpeg({ quality: 80 })
            .toBuffer();
            
        await s3.putObject({
            Bucket: bucket,
            Key: `processed/${width}/${key}`,
            Body: buffer,
            ContentType: 'image/jpeg'
        }).promise();
    });
    
    await Promise.all(promises);
    
    return { statusCode: 200 };
};

Step Functions Workflow

{
  "Comment": "Order processing state machine",
  "StartAt": "ValidateOrder",
  "States": {
    "ValidateOrder": {
      "Type": "Task",
      "Resource": "arn:aws:lambda:us-east-1:123456789:function:validate-order",
      "Next": "CheckInventory"
    },
    "CheckInventory": {
      "Type": "Task",
      "Resource": "arn:aws:lambda:us-east-1:123456789:function:check-inventory",
      "Choice": {
        "Variable": "$.inventory_available",
        "BooleanEquals": true,
        "Next": "ProcessPayment"
      },
      "Default": "NotifyOutOfStock"
    },
    "ProcessPayment": {
      "Type": "Task",
      "Resource": "arn:aws:lambda:us-east-1:123456789:function:process-payment",
      "Next": "UpdateOrder"
    },
    "UpdateOrder": {
      "Type": "Task",
      "Resource": "arn:aws:lambda:us-east-1:123456789:function:update-order",
      "Next": "NotifyCustomer"
    },
    "NotifyCustomer": {
      "Type": "Task",
      "Resource": "arn:aws:lambda:us-east-1:123456789:function:notify-customer",
      "End": true
    },
    "NotifyOutOfStock": {
      "Type": "Task",
      "Resource": "arn:aws:lambda:us-east-1:123456789:function:notify-out-of-stock",
      "End": true
    }
  }
}

Resilience Patterns

Circuit Breaker

# Circuit breaker implementation
import time
from enum import Enum

class CircuitState(Enum):
    CLOSED = "closed"      # Normal operation
    OPEN = "open"          # Failing, reject requests
    HALF_OPEN = "half_open"  # Testing recovery

class CircuitBreaker:
    def __init__(self, failure_threshold=5, timeout=60):
        self.failure_threshold = failure_threshold
        self.timeout = timeout
        self.failure_count = 0
        self.last_failure_time = None
        self.state = CircuitState.CLOSED
    
    def call(self, func, *args, **kwargs):
        if self.state == CircuitState.OPEN:
            if time.time() - self.last_failure_time > self.timeout:
                self.state = CircuitState.HALF_OPEN
            else:
                raise CircuitOpenException("Circuit is open")
        
        try:
            result = func(*args, **kwargs)
            self.on_success()
            return result
        except Exception as e:
            self.on_failure()
            raise
    
    def on_success(self):
        self.failure_count = 0
        self.state = CircuitState.CLOSED
    
    def on_failure(self):
        self.failure_count += 1
        self.last_failure_time = time.time()
        if self.failure_count >= self.failure_threshold:
            self.state = CircuitState.OPEN

Retry with Backoff

# Exponential backoff retry
import time
import random

def retry_with_backoff(func, max_retries=3, base_delay=1):
    """Retry with exponential backoff and jitter"""
    
    for attempt in range(max_retries):
        try:
            return func()
        except Exception as e:
            if attempt == max_retries - 1:
                raise
            
            # Calculate delay with jitter
            delay = base_delay * (2 ** attempt)
            jitter = random.uniform(0, delay * 0.1)
            
            print(f"Attempt {attempt + 1} failed, retrying in {delay + jitter}s")
            time.sleep(delay + jitter)

# Usage
result = retry_with_backoff(lambda: api.call())

Multi-Region Active-Active

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                  Global Architecture                        โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚                                                             โ”‚
โ”‚   โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”      โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”          โ”‚
โ”‚   โ”‚   us-east-1      โ”‚      โ”‚   eu-west-1      โ”‚          โ”‚
โ”‚   โ”‚                  โ”‚      โ”‚                  โ”‚          โ”‚
โ”‚   โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”‚      โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”‚          โ”‚
โ”‚   โ”‚  โ”‚   App      โ”‚  โ”‚      โ”‚  โ”‚   App      โ”‚  โ”‚          โ”‚
โ”‚   โ”‚  โ”‚  Cluster   โ”‚  โ”‚      โ”‚  โ”‚  Cluster   โ”‚  โ”‚          โ”‚
โ”‚   โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ”‚      โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ”‚          โ”‚
โ”‚   โ”‚         โ”‚        โ”‚      โ”‚         โ”‚        โ”‚          โ”‚
โ”‚   โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚      โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚          โ”‚
โ”‚   โ”‚  โ”‚ DynamoDB     โ”‚ โ”‚      โ”‚  โ”‚ DynamoDB    โ”‚ โ”‚          โ”‚
โ”‚   โ”‚  โ”‚ Global      โ”‚โ—€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ–ถโ”‚ Global      โ”‚ โ”‚          โ”‚
โ”‚   โ”‚  โ”‚ Table       โ”‚ โ”‚      โ”‚  โ”‚ Table       โ”‚ โ”‚          โ”‚
โ”‚   โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚      โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚          โ”‚
โ”‚   โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜      โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜          โ”‚
โ”‚            โ”‚                          โ”‚                     โ”‚
โ”‚   โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”          โ”‚
โ”‚   โ”‚              Route 53 / Cloud DNS            โ”‚          โ”‚
โ”‚   โ”‚         (Latency-based routing)              โ”‚          โ”‚
โ”‚   โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜          โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Data Architecture Patterns

Read Replica Pattern

# Database read scaling
database:
  primary:
    instance: db.r6g.xlarge
    region: us-east-1
    
  replicas:
    - instance: db.r6g.large
      region: us-east-1
      purpose: "Read scaling"
    - instance: db.r6g.large
      region: us-west-2
      purpose: "Cross-region read"
# Routing reads to replicas
class DatabaseRouter:
    def db_for_read(self, model):
        if model._meta.model_name == 'order':
            return 'replica'
        return 'primary'
    
    def db_for_write(self, model):
        return 'primary'

CQRS Pattern

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                    CQRS Architecture                        โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚                                                             โ”‚
โ”‚  Write Side:                 Read Side:                    โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”             โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”              โ”‚
โ”‚  โ”‚  Command    โ”‚             โ”‚  Query      โ”‚              โ”‚
โ”‚  โ”‚  Handler    โ”‚โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ถโ”‚  Handler    โ”‚              โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜             โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜              โ”‚
โ”‚         โ”‚                           โ”‚                      โ”‚
โ”‚         โ–ผ                           โ–ผ                      โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”             โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”              โ”‚
โ”‚  โ”‚  Primary    โ”‚โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ถโ”‚  Read       โ”‚              โ”‚
โ”‚  โ”‚  Database   โ”‚   Sync      โ”‚  Models     โ”‚              โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜             โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜              โ”‚
โ”‚                                     โ”‚                      โ”‚
โ”‚                              โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”€โ”€โ”              โ”‚
โ”‚                              โ”‚  Search     โ”‚              โ”‚
โ”‚                              โ”‚  Index     โ”‚              โ”‚
โ”‚                              โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜              โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Security Patterns

Zero Trust Network

# Zero trust network policies
network_policies:
  - name: "Deny all by default"
    type: "NetworkPolicy"
    spec:
      podSelector: {}
      policyTypes: ["Ingress", "Egress"]
      ingress: []
      egress: []
      
  - name: "Allow app-to-app"
    type: "NetworkPolicy"
    spec:
      podSelector:
        matchLabels:
          app: api
      ingress:
        - from:
            - podSelector:
                matchLabels:
                  app: frontend
          ports:
            - protocol: TCP
              port: 8080

Secrets Management

# HashiCorp Vault configuration
vault:
  auth:
    - method: "kubernetes"
      path: "kubernetes"
      config:
        kubernetes_host: "https://kubernetes.default.svc"
        
  policies:
    - name: "app-policy"
      path:
        "secret/data/myapp/*":
          capabilities: ["read"]
          
  secrets:
    - engine: "kv-v2"
      path: "secret"
      data:
        db_password: "{{db_password}}"
        api_key: "{{api_key}}"
# Using secrets in application
from hvac import Client

vault = Client(url='http://vault:8200', token='token')

def get_db_credentials():
    """Retrieve database credentials from Vault"""
    secret = vault.secrets.kv.v2.read_secret_version(
        path='database/prod',
        mount_point='secret'
    )
    return {
        'host': secret['data']['data']['host'],
        'username': secret['data']['data']['username'],
        'password': secret['data']['data']['password']
    }

Common Pitfalls

1. Monolithic Databases

Wrong:

# Shared database for all microservices
database:
  single_monolithic: true
  result: "Coupling, scaling issues, blast radius"

Correct:

# Database per service
services:
  - name: "orders"
    database: "orders-db (PostgreSQL)"
  - name: "products"
    database: "products-db (DynamoDB)"

2. Synchronous Service Communication

Wrong:

# Chained HTTP calls
def process_order(order_id):
    product = http.get(f"/products/{order_id.product_id}")
    inventory = http.get(f"/inventory/{product.sku}")
    payment = http.post("/payments", {...})
    # Slower, failure-prone

Correct:

# Event-driven async
def process_order(order_id):
    # Publish event, let services react
    event_bus.publish("order.created", order_data)
    # Faster, decoupled, resilient

3. Ignoring Cost in Architecture

Wrong:

# Over-engineered for small workload
architecture:
  microservices: true
  multi_region: true
  real_time_sync: true
  cost_per_month: "$10,000"
  actual_need: "$500"

Correct:

# Start simple, scale as needed
architecture:
  monolith_first: true
  single_region: true
  async_processing: true
  cost_per_month: "$500"
  optimize_as_grow: true

Key Takeaways

  • Start with requirements - Don’t over-engineer before understanding needs
  • Use appropriate patterns - Queue-based for decoupling, event-driven for scale
  • Embrace serverless - For variable workloads, serverless often wins on cost
  • Design for failure - Circuit breakers, retries, multi-region for resilience
  • Separate read and write - CQRS and read replicas for scaling
  • Security by default - Zero trust, secrets management, encryption
  • Think about cost - Architecture decisions directly impact cloud spend
  • Use managed services - RDS, DynamoDB, Lambda reduce operational overhead

External Resources

Architecture Guides

Pattern References

Tools

Comments