Commandはいつ使う?

Commandとは

Commandは、Application Serviceへ実行してほしいユースケースと、その実行に必要な値を表す型です。

RequestParticipationCommandという名前なら、「参加申請を実行してほしい」という要求だとコードから分かります。

CommandはApplication層に置きます。EntityやValue Objectなどのドメインモデルではなく、DDDで必ず使うものでもありません。

具体例で整理する

フットサルNOWの参加申請では、「募集ID」「参加者ID」「申請日時」をApplication Serviceへ渡します。これらを、参加申請という一つの要求を表すCommandにまとめます。

// RequestParticipationCommand.ts

// 参加申請の実行に必要な値
export type RequestParticipationCommand = {
  readonly recruitmentId: string;
  readonly participantId: string;
  readonly requestedAt: Date;
};

readonlyを付け、作った後にCommandの値を変更できないようにします。

APIからApplication Serviceへ渡す

APIはHTTPリクエストやログイン情報から必要な値を取り出し、Commandを作ってApplication Serviceへ渡します。

const command: RequestParticipationCommand = {
  recruitmentId,
  participantId: signedInUser.id,
  requestedAt: new Date(),
};

await requestParticipationApplicationService.execute(command);

Commandには、HTTPのRequestResponseを入れません。API固有の形式をApplication層へ持ち込まないためです。

Application Serviceで受け取る

export interface IRequestParticipationApplicationService {
  execute(command: RequestParticipationCommand): Promise<void>;
}

export class RequestParticipationApplicationService
  implements IRequestParticipationApplicationService {
  async execute(
    command: RequestParticipationCommand,
  ): Promise<void> {
    const recruitment = await this.recruitmentRepository.findById(
      command.recruitmentId,
    );

    if (!recruitment) {
      throw new Error("募集が見つかりません");
    }

    // 業務上の判断はドメインモデルへ任せる
    recruitment.requestParticipation({
      requesterId: new ParticipantIdValueObject(command.participantId),
      now: command.requestedAt,
    });
  }
}

Command自身は業務上の判断を行いません。Application ServiceがCommandの値を使ってドメインモデルを呼び出します。

Commandを使わなくてもよい場合

渡す値が一つだけで、型を分けても意図が分かりやすくならない場合は、Application Serviceの引数として直接受け取る方法でも問題ありません。

Commandという名前を付けることより、HTTPの入力とApplication Serviceの入力を分け、ユースケースの意図がコードから分かることが重要です。

ディレクトリで表す

src/features/futsal/
└── application/
    ├── RequestParticipationCommand.ts
    ├── IRequestParticipationApplicationService.ts
    └── RequestParticipationApplicationService.ts