Skip to main content

Interfaces

This document provides a comprehensive reference of all TypeScript interfaces used in the MBC CQRS Serverless framework.

Core Interfaces​

Command Interfaces​

CommandInputModel​

The primary interface for creating new commands. Used when publishing new entities.

export interface CommandInputModel {
pk: string // Partition key (e.g., "ORDER#tenant001")
sk: string // Sort key (e.g., "ORDER#ORD001") — no version suffix needed
id: string // Unique identifier (e.g., UUID)
code: string // Business code (e.g., "ORD001")
name: string // Display name
version: number // Version number for optimistic locking
tenantCode: string // Tenant identifier
type: string // Entity type (e.g., "ORDER")
isDeleted?: boolean // Soft delete flag
seq?: number // Sequence number (auto-generated)
ttl?: number // Time-to-live in seconds
attributes?: Record<string, any> // Custom domain attributes
}

Usage Example:

const orderInput: CommandInputModel = {
pk: 'ORDER#tenant001',
sk: 'ORDER#ORD001', // No @version suffix — version is the separate field
id: crypto.randomUUID(),
code: 'ORD001',
name: 'Customer Order',
version: 0, // VERSION_FIRST for new entities
tenantCode: 'tenant001',
type: 'ORDER',
attributes: {
customerId: 'CUST001',
totalAmount: 1500,
status: 'pending',
},
};

CommandPartialInputModel​

Used for partial updates. Only pk, sk, and version are required.

export interface CommandPartialInputModel extends Partial<CommandInputModel> {
pk: string // Required: Partition key
sk: string // Required: Sort key
version: number // Required: Current version for optimistic locking
}

Usage Example:

const partialUpdate: CommandPartialInputModel = {
pk: 'ORDER#tenant001',
sk: 'ORDER#ORD001',
version: 2, // Must match current version
name: 'Updated Order Name',
attributes: {
status: 'confirmed',
},
};

ICommandOptions​

Options passed to command publishing methods.

export interface ICommandOptions {
source?: string // Source identifier for tracking
requestId?: string // Request ID for tracing
invokeContext: IInvoke // Required: Invocation context
}

Usage Example:

const options: ICommandOptions = {
source: 'order-service',
requestId: invokeContext.context.awsRequestId,
invokeContext, // Injected via @INVOKE_CONTEXT() decorator
};

Key Interfaces​

DetailKey​

Represents the primary key for DynamoDB items.

export interface DetailKey {
pk: string // Partition key
sk: string // Sort key
}

Context Interfaces​

IInvoke​

Invocation context containing Lambda/Express event and context.

export interface IInvoke {
event?: IInvokeEvent // Request event (headers, requestContext, etc.)
context?: IInvokeContext // Lambda context (requestId, functionName, etc.)
}

IInvokeEvent​

HTTP request event structure from API Gateway or Express.

export interface IInvokeEvent {
version?: string
routeKey?: string // e.g., "POST /api/resource"
rawPath?: string
rawQueryString?: string
headers?: Record<string, string>
requestContext?: {
accountId?: string
apiId?: string
domainName?: string
domainPrefix?: string
http?: {
method?: string
path?: string
protocol?: string
sourceIp?: string // Client IP address
userAgent?: string
}
requestId?: string
stage?: string
time?: string
timeEpoch?: number
authorizer?: {
jwt?: {
claims?: JwtClaims // Decoded JWT claims
scopes?: string[]
}
}
}
isBase64Encoded?: boolean
}

IInvokeContext​

Lambda execution context information.

export interface IInvokeContext {
functionName?: string // Lambda function name
functionVersion?: string // Lambda function version
invokedFunctionArn?: string // Lambda ARN
memoryLimitInMB?: string // Memory limit
awsRequestId?: string // AWS request ID for tracing
logGroupName?: string // CloudWatch log group
logStreamName?: string // CloudWatch log stream
identity?: {
cognitoIdentityId?: string
cognitoIdentityPoolId?: string
}
}

JwtClaims​

JWT token claims structure from Cognito.

export interface JwtClaims {
sub: string // Cognito user ID (UUID)
iss: string // Token issuer URL
username?: string
'cognito:groups'?: string[] // Cognito groups the user belongs to
'cognito:username': string // Cognito username
origin_jti?: string // Original JWT ID
client_id?: string // OAuth client ID
scope?: string // OAuth scopes
aud: string // Audience (client ID)
event_id: string
token_use: string // Token type (id or access)
auth_time: number // Authentication timestamp
name: string // User's display name
'custom:tenant'?: string // Custom claim for tenant code
'custom:roles'?: string // Custom claim for roles JSON array
'custom:groups'?: string // Custom claim for tenant group memberships JSON array
exp: number // Token expiration timestamp
email: string
email_verified?: boolean
iat: number // Token issued at timestamp
jti: string // JWT ID
}

Custom Claims Example:

// The custom:roles claim contains a JSON array of role assignments
// [{"tenant":"","role":"user"},{"tenant":"9999","role":"admin"}]
// - Empty tenant ("") means the role applies globally
// - Specific tenant means the role applies only to that tenant
Version Note

The custom:groups claim was added to JwtClaims in version 1.3.1 as part of group-based role authorization.

UserContext​

User context class extracted from authentication token.

export class UserContext {
userId: string // Cognito user ID (from JWT sub claim)
tenantRole: string // User's role within the tenant
tenantCode: string // Current tenant code
tenantRoles: string[] // Direct roles for the active tenant (excludes group-derived roles)
tenantGroupIds: string[] // Group IDs from custom:groups for the active tenant

constructor(partial: Partial<UserContext>) // Initialize with partial properties
}
Version Note

tenantRoles and tenantGroupIds were added to UserContext in version 1.3.1 as part of group-based role authorization. tenantRole (singular) is kept for backward compatibility.

Usage Example:

import { getUserContext, IInvoke } from '@mbc-cqrs-serverless/core';

// Extract user context from IInvoke or ExecutionContext
const userContext = getUserContext(invokeContext);
console.log(userContext.userId); // '92ca4f68-9ac6-4080-9ae2-2f02a86206a4'
console.log(userContext.tenantCode); // 'tenant001'
console.log(userContext.tenantRole); // 'admin'

Data Interfaces​

Entity Interfaces​

CommandModel​

Interface for command entities (write model).

export interface CommandModel extends CommandInputModel {
status?: string // Processing status (e.g., 'PENDING', 'COMPLETED', 'FAILED')
source?: string // Event source identifier (e.g., 'POST /api/master', 'SQS')
requestId?: string // Unique request ID for tracing and idempotency
createdAt?: Date // Timestamp when the command was created
updatedAt?: Date // Timestamp when the command was last updated
createdBy?: string // User ID who created the command
updatedBy?: string // User ID who last updated the command
createdIp?: string // IP address of the creator
updatedIp?: string // IP address of the last updater
taskToken?: string // Step Functions task token for async workflows
syncMode?: CommandSyncMode // 'SYNC' when the command was applied via synchronous publish
}
Step Functions Integration

The taskToken field is used when integrating with AWS Step Functions callback patterns. When a Step Functions state machine invokes your application with a task token, store it using CommandService.updateTaskToken(). Later, use the token with AWS SDK's SendTaskSuccessCommand or SendTaskFailureCommand to signal task completion.

DataModel​

Base interface for data entities (read model).

export interface DataModel extends Omit<CommandModel, 'status'> {
cpk?: string // Command partition key - references source command record
csk?: string // Command sort key with version - references exact command version
}

Entity Classes​

CommandEntity​

Class implementing CommandModel for API responses. Includes Swagger decorators.

export class CommandEntity implements CommandModel {
// All properties from CommandModel
pk: string
sk: string
// ...

get key(): DetailKey // Returns { pk, sk } for DynamoDB operations
}

Usage Example:

const command: CommandEntity = await commandService.getItem({
pk: 'ORDER#tenant001',
sk: 'ORDER#ORD001',
});

// Access the DynamoDB key pair
const key = command.key; // { pk: 'ORDER#tenant001', sk: 'ORDER#ORD001' }

DataEntity​

Class implementing DataModel for API responses. Includes Swagger decorators.

export class DataEntity implements DataModel {
// All properties from DataModel
pk: string
sk: string
// ...

constructor(data: Partial<DataEntity>) // Initialize with partial properties

get key(): DetailKey // Returns { pk, sk } for DynamoDB operations
}

Usage Example:

const data: DataEntity = await dataService.getItem({
pk: 'ORDER#tenant001',
sk: 'ORDER#ORD001',
});

// Access the DynamoDB key pair
const key = data.key; // { pk: 'ORDER#tenant001', sk: 'ORDER#ORD001' }

List Response Interfaces​

DataListEntity​

Paginated list response for data queries.

export class DataListEntity {
items: DataEntity[] // Array of entities
total?: number // Total count (if available)
lastSk?: string // Pagination cursor (last sort key)

constructor(data: Partial<DataListEntity>) // Initialize with partial properties
}

Usage Example:

const result = await dataService.listItemsByPk('ORDER#tenant001', {
limit: 20,
startFromSk: previousLastSk,
});

// Pagination
if (result.lastSk) {
// More items available - use result.lastSk for next page
}

Service Interfaces​

Sync Handler Interfaces​

IDataSyncHandler​

Interface for implementing data synchronization handlers. Handles both forward (up) and rollback (down) operations.

export interface IDataSyncHandler<TExecuteResult = any, TRollbackResult = any> {
readonly type?: string // Optional type identifier

/**
* Sync data when a command is executed.
* Called automatically by the framework during command processing.
*/
up(cmd: CommandModel): Promise<TExecuteResult>

/**
* Reserved for rollback operations.
* Note: This method is NOT automatically called by the framework.
* Implement this for manual rollback scenarios in your application.
*/
down(cmd: CommandModel): Promise<TRollbackResult>
}

Implementation Example:

import { Injectable } from '@nestjs/common';
import { CommandModel, IDataSyncHandler } from '@mbc-cqrs-serverless/core';
import { PrismaService } from 'src/prisma';

@Injectable()
export class OrderRdsSyncHandler implements IDataSyncHandler {
readonly type = 'ORDER';

constructor(private readonly prisma: PrismaService) {}

async up(cmd: CommandModel): Promise<void> {
if (cmd.isDeleted) {
await this.prisma.order.delete({
where: { id: cmd.id },
});
} else {
await this.prisma.order.upsert({
where: { id: cmd.id },
create: this.toRdsModel(cmd),
update: this.toRdsModel(cmd),
});
}
}

async down(cmd: CommandModel): Promise<void> {
// Rollback logic - restore previous state
await this.prisma.order.delete({
where: { id: cmd.id },
});
}

private toRdsModel(cmd: CommandModel) {
return {
id: cmd.id,
code: cmd.code,
name: cmd.name,
...cmd.attributes,
};
}
}

Authorization Interfaces​

IGroupRoleResolver​

Version Note

IGroupRoleResolver was added in version 1.3.1 for group-based role authorization.

Interface for resolving group-to-role mappings. Implement exactly one resolver per application and register it with @GroupRoleResolver().

export interface IGroupRoleResolver {
resolveRoles(input: ResolveGroupRolesInput): Promise<string[]>
}

ResolveGroupRolesInput​

export interface ResolveGroupRolesInput {
tenantCode: string // Active tenant for which roles are being resolved
groupIds: string[] // Group IDs from the `custom:groups` JWT claim for this tenant
claims: JwtClaims // Full JWT claims for additional context
}

Implementation Example:

import {
GroupRoleResolver,
IGroupRoleResolver,
ResolveGroupRolesInput,
} from '@mbc-cqrs-serverless/core';

// Do not add @Injectable() — @GroupRoleResolver() registers a singleton provider
@GroupRoleResolver()
export class AppGroupRoleResolver implements IGroupRoleResolver {
async resolveRoles({ tenantCode, groupIds }: ResolveGroupRolesInput): Promise<string[]> {
// Map group IDs to roles — fetch from DB, config, etc.
return groupIds.includes('admin-group') ? ['admin'] : ['user'];
}
}

See Group-Based Roles for complete usage documentation.

Notification Interfaces​

EmailNotification​

Configuration for sending email notifications.

export interface EmailNotification {
fromAddr?: string // Sender address (uses default if not specified)
toAddrs: string[] // Required: Recipient addresses
ccAddrs?: string[] // CC addresses
bccAddrs?: string[] // BCC addresses
subject: string // Required: Email subject
body: string // Required: HTML body content
replyToAddrs?: string[] // Reply-to addresses
attachments?: Attachment[] // File attachments
}

export interface Attachment {
filename: string // Attachment filename
content: Buffer // File content as Buffer
contentType?: string // MIME type (e.g., 'application/pdf')
}

Usage Example:

await emailService.sendEmail({
toAddrs: ['user@example.com'],
subject: 'Order Confirmation',
body: `<h1>Order Confirmed</h1><p>Your order ${orderCode} has been confirmed.</p>`,
});

Module Configuration Interfaces​

CommandModuleOptions​

Configuration options for CommandModule.

export interface CommandModuleOptions {
tableName: string // DynamoDB table name
dataSyncHandlers?: Type<IDataSyncHandler>[] // Custom sync handlers
skipError?: boolean // Reserved for future use (not yet implemented)
disableDefaultHandler?: boolean // Disable the default DynamoDB data sync handler
}

Usage Example:

import { Module } from '@nestjs/common';
import { CommandModule } from '@mbc-cqrs-serverless/core';
import { OrderRdsSyncHandler } from './order-rds-sync.handler';

@Module({
imports: [
CommandModule.register({
tableName: 'order',
dataSyncHandlers: [OrderRdsSyncHandler],
disableDefaultHandler: false,
}),
],
})
export class OrderModule {}

SequencesModuleOptions​

Configuration options for SequencesModule.

export interface SequencesModuleOptions {
enableController?: boolean // Enable or disable default sequence controller
}

Event Interfaces​

SnsEvent​

Base interface for all SNS messages published via SnsService. Every message must include an action field. Extend this interface to add domain-specific payload fields.

export interface SnsEvent {
action: string // Message action identifier (e.g. 'order-created', 'user-updated')
}

See Queue — SnsEvent for usage examples and extension patterns.

StepFunctionsEvent​

Event structure from AWS Step Functions.

export interface StepFunctionsContextExecution {
Id: string // Execution ID
Input: { [id: string]: any } // Input data
Name: string // Execution name
RoleArn: string // IAM role ARN
StartTime: string // Execution start time
}

export interface StepFunctionsContextState {
EnteredTime: string // State entered time
Name: string // State name
RetryCount: number // Retry count
}

export interface StepFunctionsContextStateMachine {
Id: string // State machine ID
Name: string // State machine name
}

export interface StepFunctionsContext {
Execution: StepFunctionsContextExecution
State: StepFunctionsContextState
StateMachine: StepFunctionsContextStateMachine
}

export interface StepFunctionsEvent<TInput> {
input: TInput // Input data passed to the state
context: StepFunctionsContext // Step Functions context information
}

Type Utilities​

Common Type Helpers​

// Partial type that requires specific keys
type RequiredPick<T, K extends keyof T> = Partial<T> & Pick<T, K>;

// Deep partial type
type DeepPartial<T> = {
[P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : T[P];
};

// Entity without audit fields
type EntityInput<T> = Omit<T, 'createdAt' | 'createdBy' | 'updatedAt' | 'updatedBy'>;