Command パターン

操作をオブジェクトとしてカプセル化し、実行の遅延、キューイング、取り消しを可能にするデザインパターン

設計パターンアーキテクチャ

Command パターンとは

Command パターンは、操作 (メソッド呼び出し) をオブジェクトとしてカプセル化する GoF デザインパターンである。操作をオブジェクト化することで、実行の遅延、キューイング、ログ記録、取り消し (Undo) が可能になる。

基本的な実装

interface Command {
  execute(): Promise<void>;
  undo(): Promise<void>;
}

class CreateOrderCommand implements Command {
  constructor(private repo: OrderRepository, private order: Order) {}

  async execute() {
    await this.repo.save(this.order);
  }

  async undo() {
    await this.repo.delete(this.order.id);
  }
}

class UpdateStatusCommand implements Command {
  private previousStatus: string | null = null;

  constructor(private repo: OrderRepository, private orderId: string, private newStatus: string) {}

  async execute() {
    const order = await this.repo.findById(this.orderId);
    this.previousStatus = order!.status;
    order!.status = this.newStatus;
    await this.repo.save(order!);
  }

  async undo() {
    if (this.previousStatus) {
      const order = await this.repo.findById(this.orderId);
      order!.status = this.previousStatus;
      await this.repo.save(order!);
    }
  }
}

活用パターン

Undo/Redo

class CommandHistory {
  private history: Command[] = [];
  private undone: Command[] = [];

  async execute(command: Command) {
    await command.execute();
    this.history.push(command);
    this.undone = [];
  }

  async undo() {
    const command = this.history.pop();
    if (command) {
      await command.undo();
      this.undone.push(command);
    }
  }

  async redo() {
    const command = this.undone.pop();
    if (command) {
      await command.execute();
      this.history.push(command);
    }
  }
}

コマンドキュー (SQS + Lambda)

操作をシリアライズして SQS に送信し、Lambda で非同期に実行する。

CQRS との関係

CQRS (Command Query Responsibility Segregation) は、Command パターンの考え方をアーキテクチャレベルに拡張したものだ。書き込み (Command) と読み取り (Query) のモデルを分離する。

概念 Command パターン CQRS
スコープ オブジェクトレベル アーキテクチャレベル
分離 操作のカプセル化 書き込みと読み取りのモデル分離

コマンドパターンの活用

コマンドの履歴を保持すれば Undo/Redo を実現でき、コマンドをキューに入れて非同期実行することもできる。複数のコマンドを 1 つにまとめるマクロや、コマンドの実行履歴をログとして記録する用途にも応用できる。

Command パターンの関連書籍も参考になる。

関連用語