Skip to content

Commands & Command Handlers

Commands represent intent to change state in the system.

Command Interface

typescript
interface ICommand {
  commandName: string; // Required identifier
}

Example Commands

typescript
interface DepositMoneyCommand extends ICommand {
  commandName: 'DepositMoney';
  accountId: string;
  amount: number;
}

interface WithdrawMoneyCommand extends ICommand {
  commandName: 'WithdrawMoney';
  accountId: string;
  amount: number;
}

Command Handler

typescript
class DepositMoneyHandler implements ICommandHandler<DepositMoneyCommand, void> {
  constructor(private readonly repository: IAccountRepository) {}

  async execute(command: DepositMoneyCommand): Promise<void> {
    // 1. Load aggregate
    const account = await this.repository.findById(command.accountId);
    if (!account) throw new Error('Account not found');

    // 2. Execute domain logic
    account.deposit(command.amount);

    // 3. Save aggregate
    await this.repository.save(account);
  }
}

Handler Registration

typescript
const commandBus = new CommandBus();
commandBus.register('DepositMoney', new DepositMoneyHandler(repository));

Execution

typescript
await commandBus.execute({
  commandName: 'DepositMoney',
  accountId: 'acc-123',
  amount: 100
});

Best Practices

  • Commands are DTOs: Simple data transfer objects
  • Validation: Validate in handler or aggregate
  • One Handler Per Command: 1:1 mapping
  • Return Void or ID: Commands modify state, don't return data

See implementation in packages/core/src/command-bus.ts

Documentation will be expanded soon.

Released under the MIT License.