記事におけるNext.jsは、v13以降のApp Router (Server Components)を用いる前提での説明となります。
Pages Routerを用いている場合は、適宜Pages Routerでの書き方として読み替えてください。
この記事は何
最近Next.jsでウェブアプリケーションを作ることが多いのですが、
その時にオニオンアーキテクチャを採用してアプリケーションを作ることが多く、型も固まってきたので数回の記事にわたり紹介をしていきたいと思っています。
前回は
でDomain Model/Domain Serviceについて紹介しました。
今回はapplications
ディレクトリに実装していくApplication Serviceについて説明をしていきます。
Application Serviceとは
Application Serviceとは、ドメイン層に定義した処理を組み合わせることで、 Application自体のロジックの実装を行なっていくレイヤーです。
他のアーキテクチャでは「ユースケース層」などと呼ばれることもあります。
でもApplication Serviceについて紹介しています。
Application Serviceでは、以下のような処理を実装していくことになります。
- Domain ModelのインスタンスをRepositoryから取得する処理
- 取得したDomain Modelが持つメソッドの実行、組み合わせ
- 実行結果の永続化処理や、コールバック関数の実行
Repositoryや、外部のツールなどに依存する処理などはInfrastructureレイヤーになるため、Application Serviceよりさらに上のレイヤーになります。
そのため、Repositoryなどは内部で直接参照するのではなく、前回作成したRepositoryなどのインターフェースに依存するようにして、具体的なインスタンスはコンストラクタで受け取るようにします。
実装例
Application Serviceを実装していきます。
今回は「ユーザー間の送金処理」を実装していこうと思います。
「ユーザー間の送金処理」とは、以下のような処理と定義します。
- 送金元のアカウントIDと、送信先のアカウントID、送金する額(通貨)を指定する
- 受け取った値を元に、送金元のアカウントから、送金先のアカウントへ通貨を移動させる
- 移動させた結果は永続化する
- 処理完了後、送金元のアカウント、送金先のアカウントへ通知を行う
このような処理を実装すると、以下のような感じになると思います。
import { User } from '~/models/user/User'
import { UserService } from '~/models/user/UserService'
import { IUserRepository } from '~/models/user/IUserRepository'
export class UserApplicationService {
public userRepository: IUserRepository
constructor(args: {
userRepository: IUserRepository
}) {
this.userRepository = args.userRepository
}
async sendMoney(args: {
sendUserUuid: string;
receiveUserUuid: string;
money: Money;
sendNotification: (accountUuid: string) => Promise<void>;
}) {
const sendUser = await this.userRepository.findByUuid(args.sendUserUuid);
if(!sendUser) throw new Error('Send user is not found');
const receiveUser = await this.userRepository.findByUuid(args.receiveUserUuid);
if(!receiveUser) throw new Error('Receive user is not found');
const userService = new UserService()
userService.sendMoney({sendUser: sendUser, receiveUser: receiveUser, money: args.money});
await Promise.all([
this.userRepository.save(sendUser),
this.userRepository.save(receiveUser)
])
await Promise.all([
args.sendNotification(sendUser.uuid),
args.sendNotification(receiveUser.uuid)
])
}
}
最後に
今回の記事では、Application Serviceの実装を行なってきました。
次回はApplication Serviceを実行する上で必要になるInfrastructureレイヤーの実装について説明をしていきます。
今までの記事