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);
  }
}

ポイントは、

  1. RepositoryはUseCaseにDIして、ビジネスロジックを記述に使うこと
  2. 具体的な詳細は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);
}