Skip to main content

Migration Guide: v1.7.0

This guide helps you upgrade from v1.6.x to v1.7.0. Version 1.7.0 adds atomic command admission conditions to CommandService. There are no breaking changes at runtime: existing applications behave exactly as before unless they override the new hook. Some unit tests may need a small mock update.

Release Date​

2026-11-09

Changes Summary​

ChangeImpactAction Required
New getCommandAdmissionConditions hook (core)None unless overridden. The default returns no checks and keeps the plain PutItem pathNone. Optional: override it to fence command acceptance
New admission exceptions (core)Thrown only when the hook returns checksNone. Handle them if you use the hook
CommandService writes through putItemWithConditions (core)Hand-written DynamoDbService mocks with only putItem fail; createMock() auto-mocks no longer reach putItemAdd putItemWithConditions to hand-written mocks; assert on it or stub it with auto-mocks
dynamodb:ConditionCheckItem in the CLI infrastructure templateNew projects onlyAdd the action by hand if you use the hook

Pre-Migration Checklist​

  • The project already runs on v1.6.x and Node.js 22 (see the v1.6.0 migration guide)
  • You know whether your unit tests mock DynamoDbService by hand or with createMock()

Update Test Mocks​

CommandService now writes every initial command through DynamoDbService.putItemWithConditions. When no checks are returned, the real method calls putItem. Check how your tests mock DynamoDbService:

Mock styleBehavior since v1.7.0What to do
Real DynamoDbService (for example with aws-sdk-client-mock, or with jest.spyOn on putItem)Keeps working: with no conditions, putItemWithConditions delegates to putItemNothing
Hand-written mock that only defines putItemFails with putItemWithConditions is not a functionAdd putItemWithConditions
Auto-mock from createMock() (@golevelup/ts-jest)Does not throw, but putItemWithConditions is auto-mocked and never reaches putItem. Assertions on putItem fail without an obvious reasonAssert on putItemWithConditions, or stub it to call putItem

For a hand-written mock:

// Before (v1.6.x)
const dynamoDbService = {
getItem: jest.fn(),
putItem: jest.fn(async (tableName, item) => item),
}

// After (v1.7.0)
const dynamoDbService = {
getItem: jest.fn(),
putItem: jest.fn(async (tableName, item) => item),
putItemWithConditions: jest.fn(function (tableName, item, condition) {
return this.putItem(tableName, item, condition)
}),
}

For a createMock() auto-mock, assert on putItemWithConditions or stub it to call putItem; see Unit Tests for both options. The framework verified this behavior in PR #516.

New Feature: Command Admission Conditions​

A CommandService subclass can override the protected getCommandAdmissionConditions(command, options) method and return DynamoDB ConditionCheck entries. The framework writes them in the same TransactWriteItems request as the initial command, so the command is accepted only if every check passes. A typical use is freezing configuration updates during maintenance while other commands continue, without the race of a preflight gate check.

When the transaction is cancelled, the framework throws:

ExceptionHTTPWhen
CommandAdmissionRejectedException409An admission check failed
ConditionalCheckFailedException409The command write itself had a version conflict
CommandAdmissionContentionException503The transaction was cancelled by TransactionConflict
TransactionCanceledException500Any other cancellation

See Command Admission Conditions for the hook, an example subclass and provider registration.

Before Using the Hook​

  • IAM: grant dynamodb:ConditionCheckItem on every witness table. Projects generated with mbc new before v1.7.0 do not have it. Add it to the DynamoDB policy in infra/libs/infra-stack.ts (see CDK Infrastructure). Without it, protected commands fail with an access-denied error.
  • Capacity: every item in a transaction consumes double write capacity, even when the transaction is cancelled.
  • Contention: concurrent commands that check the same witness item can cancel each other with TransactionConflict on the DynamoDB service. This has not been measured, and DynamoDB Local does not reproduce it. Load test the expected concurrency in a real environment.

CLI Templates (New Projects Only)​

FileBeforeAfter (v1.7.0)
infra/libs/infra-stack.tsdynamodb:PutItem, UpdateItem, GetItem, QueryAlso dynamodb:ConditionCheckItem

Existing projects are not changed. The extra action is needed only if you use the hook.

Upgrade Steps​

Step 1: Update Dependencies​

npm install \
@mbc-cqrs-serverless/core@^1.7.0 \
@mbc-cqrs-serverless/tenant@^1.7.0 \
@mbc-cqrs-serverless/master@^1.7.0 \
@mbc-cqrs-serverless/sequence@^1.7.0 \
@mbc-cqrs-serverless/task@^1.7.0 \
@mbc-cqrs-serverless/cli@^1.7.0

Step 2: Update Test Mocks​

Add putItemWithConditions to hand-written DynamoDbService mocks, and update putItem assertions that rely on createMock() auto-mocks (see Update Test Mocks).

Step 3: Run Tests and Deploy​

npm run test:cov

Support​

If you encounter issues during migration: