UseCase
UseCaseは、ビジネスロジックを記述するためのモジュールです。 UseCaseは一般的に、Entityを利用して、ビジネスロジックを実行して、結果を返したり、Repositoryを利用して、データを永続化したりします。
なぜUseCaseが必要なのか?
UseCaseが無いプロジェクトでは、ビジネスロジックがAPIやViewのコントローラーなどに、直接記述されることがあります。
それらのAPIのコントローラー責務は、HTTPリクエストを受けて適切なレスポンスであるし、ViewのコントローラーはViewの動作を制御する責務を担います。
そのコントローラーに直接ビジネスロジックを記述することは、Fat Controllerと呼ばれ、コードが肥大化してしまうおそれがあります。
そこで、ビジネスロジックを記述するためのモジュールとしてUseCaseを導入することで、ビジネスロジックを責務の違うモジュールと混同させないことが可能となります。
UseCaseの実装例
Dart言語では、次のようなインターフェースでUseCaseを定義することが多いです。
class UserUseCase {
final UserRepository userRepository;
UserUseCase(this.userRepository);
Future<User> updateLastLoginAt(UserId id) async {
final user = await userRepository.findById(id);
if (user == null) {
throw Exception('User not found');
}
final newUser = user.updateLastLoginAt();
return userRepository.save(newUser);
}
}ポイントは、
- RepositoryはUseCaseにDIして、ビジネスロジックを記述に使うこと
- 具体的な詳細はEntityのメソッドに書いて、UseCaseはビジネスロジックの詳細については、知らないこと
の2点です。 UseCaseはビジネスロジックの記述を担う責務ではありますが、具体的な処理内容はそれぞれ、RepositoryやEntityに任せることを推奨します。
もしも、UseCaseに具体的な処理がたくさん記述されている場合、ドメインモデル貧血症と言って、ドメインモデルが機能していない可能性があります。
UseCaseの粒度
プロジェクトのサイズによって、UseCaseの粒度は異なります。
小さなプロジェクト
小さなプロジェクトであっても、UseCaseがあったほうが見通しが良くなるため、UseCaseを導入することを推奨します。 しかし、小さなプロジェクトでは、UseCaseのクラスが多すぎて、逆に認知不可が上がってしまうということもあります。
そこで、小さなプロジェクトでは、UserUseCaseのように、ある程度Entity毎にまとめて、UseCaseを定義するようにしています。
大きなプロジェクト
大きなプロジェクトになると、一つのEntityに関連するUseCaseでも、たくさんのビジネスロジックが存在するようなケースがあります。
そのような場合には、CreateUserUseCaseのように、1つのビジネスロジックに、1つのクラスを対応させることで、ビジネスロジックの可読性を保つことが可能です。
Dartでは、以下のような、インターフェースになります。
class CreateUserUseCase {
final UserRepository userRepository;
CreateUserUseCase(this.userRepository);
Future<User> execute(User user);
}