DEV Community

myougaTheAxo
myougaTheAxo

Posted on

Claude Codeでイベント駆動ステートマシンを設計する:注文ワークフロー・状態遷移の型安全な管理

はじめに

「注文ステータスが不正な遷移になっている」——TypeScriptの型システムを使ったステートマシンで状態遷移を安全に管理し、不正遷移をコンパイル時に検出する設計をClaude Codeに生成させる。


CLAUDE.mdに設計ルールを書く

## イベント駆動ステートマシン設計ルール

- 全状態をUnion型で定義('pending' | 'confirmed' | 'shipped' | ...)
- 遷移マップで許可された遷移のみを定義(それ以外は型エラー)
- 状態変更はDB更新とイベントログをアトミックに(トランザクション)
- ガード条件: 遷移前に前提条件を検証
- イベントログから現在の状態を再構築可能(Event Sourcing)
Enter fullscreen mode Exit fullscreen mode

生成される実装(抜粋)

// 状態と遷移マップの定義
export type OrderState = 'draft' | 'pending_payment' | 'confirmed' | 'shipped' | 'delivered' | 'cancelled' | 'refunded';
export type OrderEvent = { type: 'SUBMIT'; actorId: string } | { type: 'PAYMENT_RECEIVED'; paymentId: string; actorId: string } | ...;

const TRANSITIONS: TransitionMap = {
  draft:           { SUBMIT: 'pending_payment' },
  pending_payment: { PAYMENT_RECEIVED: 'confirmed', CANCEL: 'cancelled' },
  confirmed:       { START_PREPARING: 'preparing', CANCEL: 'cancelled' },
  shipped:         { DELIVER: 'delivered' },
};

// 型安全な状態遷移(ガード条件付き)
async transition(orderId, event, guards?) {
  return prisma.$transaction(async (tx) => {
    const order = await tx.$queryRaw`SELECT * FROM orders WHERE id = ${orderId} FOR UPDATE`.then(r => r[0]);
    if (!this.machine.canTransition(order.status, event)) throw new InvalidStateTransitionError(...);
    if (guards?.before) await guards.before(); // ガード失敗→エラー→状態変更なし

    const nextState = this.machine.getNextState(order.status, event);

    // イベントログ記録(Event Sourcing)
    await tx.orderEvent.create({ data: { orderId, eventType: event.type, fromState: order.status, toState: nextState, actorId: event.actorId } });
    return tx.order.update({ where: { id: orderId }, data: { status: nextState } });
  });
}

// ガード条件付き使用例
await service.transition(orderId, { type: 'CONFIRM', actorId: 'admin-1' }, {
  before: async () => {
    if (!await inventoryService.checkAllItems(orderId)) throw new InsufficientInventoryError('Cannot confirm: out of stock');
  },
});
Enter fullscreen mode Exit fullscreen mode

まとめ

  1. CLAUDE.md に全状態をUnion型で定義・遷移マップで許可遷移のみ定義・状態変更とイベントログをトランザクションで原子実行を明記
  2. TRANSITIONS遷移マップ でどの状態からどのイベントでどの状態へ遷移できるかを宣言的に定義——コードを見れば全遷移が一目瞭然
  3. ガード条件 で在庫確認・残高確認などを遷移前に検証——ガード失敗時はエラーをスローして状態を変更しない
  4. イベントログ(Event Sourcing) で全遷移を記録——rebuildStateで任意の時点の状態を再構築でき、デバッグ・監査ログとして活用

アーキテクチャ設計のレビューは **Code Review Pack(¥980)* の /code-review で確認できます。*

prompt-works.jp

みょうが (@myougatheaxo) — ウーパールーパーのVTuber。

Top comments (0)