Skip to main content

DynamoDB Store

Serverless store using AWS DynamoDB with auto-scaling.

Installation​

npm install @saga-bus/store-dynamodb @aws-sdk/client-dynamodb @aws-sdk/lib-dynamodb

Basic Usage​

import { DynamoDBSagaStore } from '@saga-bus/store-dynamodb';

const store = new DynamoDBSagaStore({
tableName: 'sagas',
region: 'us-east-1',
});

const bus = createBus({
transport,
store,
sagas: [{ definition: orderSaga }],
});

await bus.start();

Configuration​

OptionTypeDefaultDescription
tableNamestringRequiredDynamoDB table name
regionstringRequiredAWS region
endpointstring-Custom endpoint (LocalStack)
credentialsobject-AWS credentials
clientDynamoDBClient-Existing DynamoDB client

Full Configuration Example​

import { DynamoDBSagaStore, createTable } from '@saga-bus/store-dynamodb';
import { DynamoDBClient } from '@aws-sdk/client-dynamodb';
import { fromIni } from '@aws-sdk/credential-providers';

// Option 1: Basic configuration
const store = new DynamoDBSagaStore({
tableName: 'sagas',
region: 'us-east-1',
});

// Option 2: With credentials
const store = new DynamoDBSagaStore({
tableName: 'sagas',
region: 'us-east-1',
credentials: fromIni({ profile: 'production' }),
});

// Option 3: Existing client
const client = new DynamoDBClient({
region: 'us-east-1',
maxAttempts: 3,
});

const store = new DynamoDBSagaStore({
client,
tableName: 'sagas',
});

// Create table if needed
await createTable(store);

Table Schema​

Primary Key Design​

Partition Key (PK): sagaName
Sort Key (SK): sagaId

Global Secondary Index​

GSI: correlationIndex
Partition Key: sagaName
Sort Key: correlationId

CloudFormation/CDK​

# CloudFormation
Resources:
SagasTable:
Type: AWS::DynamoDB::Table
Properties:
TableName: sagas
BillingMode: PAY_PER_REQUEST
AttributeDefinitions:
- AttributeName: sagaName
AttributeType: S
- AttributeName: sagaId
AttributeType: S
- AttributeName: correlationId
AttributeType: S
KeySchema:
- AttributeName: sagaName
KeyType: HASH
- AttributeName: sagaId
KeyType: RANGE
GlobalSecondaryIndexes:
- IndexName: correlationIndex
KeySchema:
- AttributeName: sagaName
KeyType: HASH
- AttributeName: correlationId
KeyType: RANGE
Projection:
ProjectionType: ALL
TimeToLiveSpecification:
AttributeName: ttl
Enabled: true

Terraform​

resource "aws_dynamodb_table" "sagas" {
name = "sagas"
billing_mode = "PAY_PER_REQUEST"
hash_key = "sagaName"
range_key = "sagaId"

attribute {
name = "sagaName"
type = "S"
}

attribute {
name = "sagaId"
type = "S"
}

attribute {
name = "correlationId"
type = "S"
}

global_secondary_index {
name = "correlationIndex"
hash_key = "sagaName"
range_key = "correlationId"
projection_type = "ALL"
}

ttl {
attribute_name = "ttl"
enabled = true
}
}

LocalStack Development​

For local development:

const store = new DynamoDBSagaStore({
tableName: 'sagas',
region: 'us-east-1',
endpoint: 'http://localhost:4566',
credentials: {
accessKeyId: 'test',
secretAccessKey: 'test',
},
});

Docker Compose:

services:
localstack:
image: localstack/localstack:latest
ports:
- "4566:4566"
environment:
- SERVICES=dynamodb
- DEFAULT_REGION=us-east-1

Optimistic Concurrency​

Conditional updates with version:

// Built-in optimistic locking
// Uses ConditionExpression to check version
// Throws ConditionalCheckFailedException on conflict

TTL for Completed Sagas​

Enable automatic cleanup:

const store = new DynamoDBSagaStore({
tableName: 'sagas',
region: 'us-east-1',
ttlDays: 30, // Auto-delete after 30 days
});

IAM Permissions​

Required IAM policy:

{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"dynamodb:GetItem",
"dynamodb:PutItem",
"dynamodb:UpdateItem",
"dynamodb:DeleteItem",
"dynamodb:Query"
],
"Resource": [
"arn:aws:dynamodb:us-east-1:123456789012:table/sagas",
"arn:aws:dynamodb:us-east-1:123456789012:table/sagas/index/*"
]
}
]
}

Best Practices​

Use On-Demand Billing​

// PAY_PER_REQUEST for variable workloads
// No capacity planning needed

Enable Point-in-Time Recovery​

PointInTimeRecoverySpecification:
PointInTimeRecoveryEnabled: true

Use DynamoDB Streams for Events​

// Stream changes for audit/analytics
StreamSpecification:
StreamViewType: NEW_AND_OLD_IMAGES

Global Tables​

For multi-region deployments:

// Configure global tables in AWS Console or IaC
// Store automatically works with global tables

Cost Optimization​

  • Use on-demand for unpredictable workloads
  • Use provisioned for steady workloads
  • Enable TTL to auto-delete old data
  • Use sparse indexes

See Also​