ページ一覧を開く

Application Service interface

DDDでの位置づけ

Application Serviceのinterfaceは、DDDで必ず作るものではありません。

このガイドラインでは、APIが何を呼び出せるか、何を渡し、何が返るかを、実装を読まなくても分かるようにするため、interfaceを作ります。

interfaceで決めること

interfaceには、呼び出せるメソッド、そのメソッドへ渡す情報、処理後に返す結果を定義します。処理の内容は書きません。

参加申請では、IRequestParticipationApplicationServiceを呼び出し口にします。

コードで表す

// IRequestParticipationApplicationService.ts

// execute()に渡す参加申請の情報
export type RequestParticipationInput = {
  recruitmentId: string; // 参加したい募集のID
  participantId: string; // 参加を申請する人のID
  now: Date;             // 参加を申請した日時
};

// 参加申請ユースケースの呼び出し口
export interface IRequestParticipationApplicationService {
  // 参加申請を実行する
  execute(input: RequestParticipationInput): Promise<void>;
}

execute()へ渡す情報はRequestParticipationInputで決めています。返却値はPromise<void>なので、この例では処理結果のデータを返しません。

Application Serviceが実装する

RequestParticipationApplicationServiceは、IRequestParticipationApplicationServiceで決めたexecute()を実装します。

import type {
  RequestParticipationInput,
  IRequestParticipationApplicationService,
} from "./IRequestParticipationApplicationService";

export class RequestParticipationApplicationService
  implements IRequestParticipationApplicationService {
  async execute(input: RequestParticipationInput): Promise<void> {
    // 参加申請ユースケースの処理
  }
}

interfaceを通して呼び出す

APIは、実装クラスではなくIRequestParticipationApplicationServiceを受け取り、execute()を呼び出します。

// APIがinterfaceとしてApplication Serviceを受け取る
async function handleRequest(
  applicationService: IRequestParticipationApplicationService,
): Promise<void> {
  await applicationService.execute({
    recruitmentId,
    participantId: signedInUser.id,
    now: new Date(),
  });
}

APIは、参加申請がどのクラスで実装されているかを意識せず、interfaceで決めた呼び出し方だけを使います。

ディレクトリで表す

src/features/futsal/
└── application/
    ├── IRequestParticipationApplicationService.ts  ← interface
    └── RequestParticipationApplicationService.ts   ← 実装

このガイドラインでは、interfaceの先頭にIを付けます。実装クラスにはIを付けず、ファイル名だけで役割を見分けられるようにします。