little-hands/ddd-q-and-a

ユースケースは、ドメイン層のリポジトリ(インターフェース)を使用しているので、プレゼンテーション層からユースケース層を呼び出す際に、実体としてインフラ層のリポジトリをnewなどして渡してあげる必要があると思います。これはプレゼンテーション層...

Opened this issue · 1 comments

Question

ユースケースは、ドメイン層のリポジトリ(インターフェース)を使用しているので、プレゼンテーション層からユースケース層を呼び出す際に、実体としてインフラ層のリポジトリをnewなどして渡してあげる必要があると思います。
これはプレゼンテーション層からインフラ層への依存になってしまうと思うのですが、回避できますでしょうか?
長くなって申し訳ありません、よろしくお願いします。
例:
class TaskCreateController {
public index(params: {keyA: string}) {
// usecaseはinterfaceを使っているので、実装されたrepositoryの実体を渡してあげる必要がある
// しかしここでnewをしてしまうと、プレゼンテーション層がインフラ層に依存してしまう
const mysqlRepository = new MysqlRepository();
const taskCreateUseCase = new TaskCreateUseCase(params);
taskCreateUseCase.execute(mysqlRepository);
}
}

Answer

これはDI(DependencyInjection)コンテナがあれば解決してくれる問題ではあるのですが、言語やフレームワーク的にDIコンテナを使用できない場合は自分でどうにかするしかないですね。
一つは実害の有無を考えて、コントローラーで直接newすることを許容するパターンです。(ご質問の実装)この実装にするだけでも、ユースケースとしてはユニットテストでIFの実装クラスを外から差し替えられるという大きなメリットがあります。コントローラーで使用するクラスを直接指定するのは気持ち悪いと思いますが、実際大きな実害があるか・・・と考えると意外と許容できる、となる可能性はあるかと思います。
もうひとつは、DIコンテナの簡易版のようなものを自作する形ですね。コントローラーが直接newするのではなく、ユースケースに渡すインスタンスを生成する別のクラスを作るのです。そうするとコントローラーからは直接的なクラスを知らずに済むので、違和感がかなり減らせると思います。

DIコンテナがなくても、プレゼンテーション層がインフラストラクチャ層に依存しないようにできますね。

package domain.task

interface TaskRepository {
   fun findById(taskId: TaskId): Task
}
package infrastructure.repository.task

// インフラストラクチャ層のリポジトリ実装が依存するのはドメイン層のリポジトリI/F
class TaskRepositoryOnMySQL : TaskRepository {
  // ...
}
package use.case.task

// ユースケース層のユースケースが依存するのはドメイン層のリポジトリI/F
class TaskCreateUseCase(val taskRepository: TaskRepository) {
  fun execute(taskId: TaskId): Task {
    // ...
  }
}
package presentation.controllers.task

// プレゼンテーション層のコントローラが依存するのはユースケース層のユースケースクラス。
class TaskCreateController(val useCase: TaskCreateUseCase) {
  fun index(params: Parms):  Action {
       val result = useCase.execute(params.taskId)
       result.toJson
  }
}

初期化時にDIコンテナを使わなくても、DI可能です。

val taskRepository: TaskRepository = TaskRepositoryOnMySQL(...)
val taskCreateUseCase: TaskCreateUseCase = TaskCreateUseCase(taskRepository)
val taskCreateController: TaskCreateController = TaskCreateController(taskCreateUseCase)

ということで、プレゼンテーション層がインフラストラクチャ層に依存しないで済みます。