Transaction
Transactionとは
Transactionは、一つのユースケースで行うDBへの変更を、まとめて確定または取り消す仕組みです。すべての変更に成功した場合だけ確定し、途中で失敗した場合は、それまでの変更も取り消します。
どこからどこまでを一つのTransactionとして扱うか、その範囲を「Transaction境界」と呼びます。
具体例で整理する
例えば、フットサルNOWで参加申請を受け付けると、「募集」に参加者が追加され、その結果がDBへ保存されます。
保存の途中で失敗したときに、一部のデータだけが更新されると、募集と参加者の情報が合わなくなります。そのため、参加申請で行う変更を一つのTransactionとして扱い、まとめて確定または取り消します。
Application Serviceで範囲を決める
このガイドラインでは、Application Serviceが実行する一つのユースケースを、Transactionの基本的な範囲にします。
// 参加申請のユースケースを実行する
async execute(command: RequestParticipationCommand): Promise<void> {
// この中で行うDBへの変更を、一つのTransactionとして扱う
await this.transaction.run(async () => {
// 参加したい募集を取得する
const recruitment = await this.recruitmentRepository.findById(
command.recruitmentId,
);
if (!recruitment) {
throw new Error("募集が見つかりません");
}
// RecruitmentEntityが参加申請のルールを確認し、参加者を追加する
recruitment.requestParticipation({
requesterId: new ParticipantIdValueObject(command.participantId),
now: command.requestedAt,
});
// 変更した募集をDBへ保存する
await this.recruitmentRepository.save(recruitment);
});
}transaction.run()の内側が、この例のTransaction境界です。処理が最後まで成功すれば変更を確定し、例外が発生すれば変更を取り消します。
DDDでの役割分担
Application Serviceは、どの処理を一つのTransactionに含めるかを決めます。EntityやValue Objectは、Transactionの開始や確定を行いません。
DBを使ってTransactionを開始し、確定または取り消す具体的な処理は、Infrastructure側で実装します。これにより、ドメインモデルへDB固有の処理を持ち込まずに済みます。
Aggregateとの関係
このガイドラインでは、原則として一つのTransactionで一つのAggregateを変更します。参加申請では、「募集」のAggregateを取得し、ルールを確認して変更し、そのAggregateを保存します。
複数のAggregateを一つのTransactionで変更すると、処理の範囲や影響が大きくなります。複数のAggregateを扱う必要がある場合は、本当に同時に確定する必要があるかを確認します。