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
| Change | Impact | Action Required |
|---|---|---|
New getCommandAdmissionConditions hook (core) | None unless overridden. The default returns no checks and keeps the plain PutItem path | None. Optional: override it to fence command acceptance |
| New admission exceptions (core) | Thrown only when the hook returns checks | None. 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 putItem | Add putItemWithConditions to hand-written mocks; assert on it or stub it with auto-mocks |
dynamodb:ConditionCheckItem in the CLI infrastructure template | New projects only | Add 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
DynamoDbServiceby hand or withcreateMock()
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 style | Behavior since v1.7.0 | What 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 putItem | Nothing |
Hand-written mock that only defines putItem | Fails with putItemWithConditions is not a function | Add 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 reason | Assert 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:
| Exception | HTTP | When |
|---|---|---|
CommandAdmissionRejectedException | 409 | An admission check failed |
ConditionalCheckFailedException | 409 | The command write itself had a version conflict |
CommandAdmissionContentionException | 503 | The transaction was cancelled by TransactionConflict |
TransactionCanceledException | 500 | Any other cancellation |
See Command Admission Conditions for the hook, an example subclass and provider registration.
Before Using the Hook
- IAM: grant
dynamodb:ConditionCheckItemon every witness table. Projects generated withmbc newbefore v1.7.0 do not have it. Add it to the DynamoDB policy ininfra/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
TransactionConflicton 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)
| File | Before | After (v1.7.0) |
|---|---|---|
infra/libs/infra-stack.ts | dynamodb:PutItem, UpdateItem, GetItem, Query | Also 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:
Related Documentation
- Changelog v1.7.0 - v1.7.0 release notes
- CommandService - Command admission conditions
- Error Catalog - Command admission exceptions
- Unit Tests - Mocking
DynamoDbServicefor CommandService