Building a Multi-Region Active-Active API Architecture on AWS with Amazon DynamoDB Global Tables and Route 53
This advanced guide provides a complete architectural blueprint and production-grade AWS CDK v2 infrastructure code in TypeScript to deploy a resilient, multi-region API topology. We will utilize Amazon Route 53 latency routing, Amazon API Gateway, AWS Lambda, and Amazon DynamoDB Global Tables to handle multi-region data replication and conflict resolution.
1. Multi-Region Active-Active Topology
An Active-Active architecture requires synchronous or near-synchronous data availability across disparate geographic regions, combined with an intelligent DNS routing layer that handles failure detection and edge optimization.
[ Global User Base ]│[ Amazon Route 53 Traffic Flow ]/ (Latency + Health Checks)▼ ▼[ us-east-1 (Primary Base) ] [ eu-west-1 (Secondary Base) ]├── Amazon API Gateway ├── Amazon API Gateway└── AWS Lambda Compute └── AWS Lambda Compute\ /▼ ▼[ Amazon DynamoDB Global Table (Bi-Directional Sync) ]
Core Architecture Components
- Intelligent Edge Routing: Amazon Route 53 evaluates incoming user DNS queries using Latency-Based Routing. Traffic is routed dynamically to the region that offers the lowest round-trip time.
- Decoupled Edge Compute: Stateless API Gateways and Lambda functions are deployed symmetrically across your chosen target regions (e.g.,
us-east-1andeu-west-1). - Global Persistence Layer: Amazon DynamoDB Global Tables automate fully managed, bi-directional multi-master replication. Writes to any region are asynchronously replicated to all other participant regions, typically in less than a second.
2. Infrastructure as Code: Multi-Region AWS CDK Stack
To safely deploy cross-region resources via the AWS CDK, we utilize a design pattern where a single repository controls a deployment pipeline or multi-stack instantiation tailored to independent regional environments.
Save the following implementation as
global-database-stack.ts and regional-api-stack.ts.Part A: The Global Persistence Layer (global-database-stack.ts)
DynamoDB Global Tables must be defined globally before regional application resources attempt to map to them.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import * as cdk from 'aws-cdk-lib';
import * as dynamo from 'aws-cdk-lib/aws-dynamodb';
import { Construct } from 'constructs';
export class GlobalDatabaseStack extends cdk.Stack {
public readonly globalTable: dynamo.TableV2;
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
// Global DynamoDB Table configured for Multi-Region Master Replication
this.globalTable = new dynamo.TableV2(this, 'GlobalEnterpriseTable', {
tableName: 'enterprise-global-datastore',
partitionKey: { name: 'pk', type: dynamo.AttributeType.STRING },
sortKey: { name: 'sk', type: dynamo.AttributeType.STRING },
billing: dynamo.Billing.onDemand(),
pointInTimeRecovery: true,
timeToLiveAttribute: 'ttl',
// Define the replica regions explicitly
replicas: [
{ region: 'us-east-1' },
{ region: 'eu-west-1' },
],
removalPolicy: cdk.RemovalPolicy.RETAIN, // Safeguard production data
});
}
}Part B: The Regional API Layer (regional-api-stack.ts)
This stack is instantiated twice in your CDK application entry point—once for each target deployment region.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
import * as cdk from 'aws-cdk-lib';
import * as lambda from 'aws-cdk-lib/aws-lambda';
import * as apigateway from 'aws-cdk-lib/aws-apigateway';
import * as iam from 'aws-cdk-lib/aws-iam';
import { Construct } from 'constructs';
export interface RegionalApiStackProps extends cdk.StackProps {
readonly targetRegion: string;
}
export class RegionalApiStack extends cdk.Stack {
constructor(scope: Construct, id: string, props: RegionalApiStackProps) {
super(scope, id, props);
// 1. Regional Stateless Compute Layer
const apiHandler = new lambda.Function(this, 'RegionalApiHandler', {
runtime: lambda.Runtime.NODEJS_20_X,
architecture: lambda.Architecture.ARM_64,
handler: 'index.handler',
environment: {
AWS_NODEJS_CONNECTION_REUSE_ENABLED: '1',
CURRENT_REGION: props.targetRegion,
DYNAMODB_TABLE_NAME: 'enterprise-global-datastore',
},
code: lambda.Code.fromInline(`
const { DynamoDBClient } = require("@aws-sdk/client-dynamodb");
const { DynamoDBDocumentClient, PutCommand } = require("@aws-sdk/lib-dynamodb");
const client = new DynamoDBClient({});
const docClient = DynamoDBDocumentClient.from(client);
exports.handler = async (event) => {
console.log("Processing request in region:", process.env.CURRENT_REGION);
const timestamp = new Date().toISOString();
const payload = JSON.parse(event.body || "{}");
const command = new PutCommand({
TableName: process.env.DYNAMODB_TABLE_NAME,
Item: {
pk: payload.userId || "anonymous",
sk: \`TRANSACTION#\${timestamp}\`,
regionOrigin: process.env.CURRENT_REGION,
amount: payload.amount || 0,
createdAt: timestamp
}
});
await docClient.send(command);
return {
statusCode: 200,
headers: { "Content-Type": "application/json", "X-Handled-By": process.env.CURRENT_REGION },
body: JSON.stringify({ status: "Success", processedIn: process.env.CURRENT_REGION })
};
};
`),
});
// Grant exact Least-Privilege IAM execution permissions for the regional replica
apiHandler.addToRolePolicy(new iam.PolicyStatement({
actions: [
'dynamodb:PutItem',
'dynamodb:GetItem',
'dynamodb:UpdateItem',
'dynamodb:Query',
],
resources: [
\`arn:aws:dynamodb:${props.targetRegion}:\${this.account}:table/enterprise-global-datastore\`
],
}));
// 2. REST API Gateway Edge Construct
const restApi = new apigateway.RestApi(this, 'RegionalGatewayEndpoint', {
restApiName: `enterprise-api-${props.targetRegion}`,
deployOptions: {
stageName: 'prod',
metricsEnabled: true,
dataTraceEnabled: false,
},
});
const transactions = restApi.root.addResource('transactions');
transactions.addMethod('POST', new apigateway.LambdaIntegration(apiHandler));
}
}3. Mission-Critical Data Strategies: Managing Concurrency & Conflict
While DynamoDB Global Tables manage physical block replication automatically, software architects must account for logical edge cases driven by Distributed Systems mechanics (e.g., the CAP Theorem).
Last-Write-Wins (LWW) Resolution
DynamoDB coordinates concurrent updates across multiple regions using a Last-Write-Wins (LWW) conflict resolution strategy based on UTC timestamps. If a user updates the same record database fields in
us-east-1 and eu-west-1 simultaneously within a small millisecond replication window, the write with the higher timestamp wins, overwriting the alternate update.- Mitigation (Deterministic Keys): To prevent unpredictable overwrites, decouple independent regional actions into separate ledger items. In our CDK Lambda code block above, notice that the Sort Key (
sk) utilizes an atomic timestamp token concatenated directly into the primary key schema:TRANSACTION#${timestamp}. This structure turns potential updates into unique append-only writes, entirely side-stepping LWW race conditions.
Read Consistency Models
DynamoDB Global Tables support Strongly Consistent Reads only within the same physical region where the write operation occurred. If your application logic requires an immediate read of a data write that happened across the world, cross-region reads are strictly Eventually Consistent. Build retry mechanics into your frontend clients or employ idempotent query structures to gracefully manage this replication delay.
4. Disaster Recovery (DR) Verification Lifecycle
Deploying an Active-Active setup means designing for failure. To make sure your multi-region environment is actually reliable, use this testing lifecycle:
- Synthetic Endpoint Verification: Deploy Route 53 Health Checks to monitor the health of your regional API endpoints (
/healthz). Set the evaluation period to an aggressive 10-second interval. - Automated Cross-Region Failover: When Route 53 detects that a regional API Gateway is throwing 5XX errors or timing out, it modifies global DNS routing tables within seconds. Live user traffic is immediately routed away from the impaired region and sent to the remaining healthy node.
- Chaos Testing via Fault Injection: Periodically simulate a full regional failure by revoking the API Gateway's IAM permissions or dropping incoming security group rules. Verify that your client applications fail over automatically to the alternate geographic node without data corruption or dropped requests.
#devops #aws-cdk #dynamodb-global-tables #multi-region #active-active #system-design
Enjoyed reading this content? Let the author know!
Your likes, comments, shares, and saves help creators reach more builders.
Loading recommendations
Loading article