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 パターンの関連書籍も参考になる。