5/24 ドメイン駆動設計 Chapter7 7-2
7-2依存とは
依存は、あるオブジェクトがあるオブジェクトを参照するだけで発生します。
class ObjectA { constructor(private objectB: ObjectB) {} }
- ObjectAはObjectBを参照しています
- ObjectBの定義が存在しない限りObjectAも成り立ちません
- ObjectAはObjectBに依存していると言えます
- ObjectBの定義が存在しない限りObjectAも成り立ちません
依存関係は図で表すことができる
- 依存は依存する側のモジュールから依存される側のモジュールへと矢印を伸ばして表現する
依存関係は参照のみではなく、インターフェースとその実体になる実装クラスの関係でも発生します。
interface IUserRepository { save(user: User): void; find(id: UserId): Promise<User | null>; }
class UserRepository implements IUserRepository { constructor(private prisma: PrismaClient) {} async save(user: User): Promise<void> { const { id, name } = user; await this.prisma.user.create({ data: { id: id, name: name, }, }); } async find(id: UserId): Promise<User | null> { const user = await this.prisma.user.findUnique({ where: { id: id, }, }); return user ? new User(user.id, user.name) : null; } }
- UserRepositoryクラスはIUserRepositoryインターフェースを実装しています
- IUserRepositoryの定義が存在しなかったとしたらクラスの宣言部でコンパイルエラーになりUserRepositoryが成り立ちません
- UserRepositoryはIUserRepositoryに依存していることになる
- IUserRepositoryの定義が存在しなかったとしたらクラスの宣言部でコンパイルエラーになりUserRepositoryが成り立ちません
class UserApplicationService { constructor(private readonly userRepository: UserRepository) {} // 省略 }
- UserApplicationServiceにはUserRepositoryがフィールドとして定義されている
- UserApplicationServiceはUserRepositoryに依存している状態です
- これは5章でやったように、実体クラスをそのまま使うのではなく、インターフェースを介してUserRepositoryを利用するのがいいです
class UserApplicationService { constructor(private readonly userRepository: IUserRepository, private readonly userService: UserService) {} // 省略 }
- IUserRepositoryを受け取るようにする
- 抽象型を利用すると、具象型に向いていた依存の矢印が抽象型に向くようになります
- 依存の方向性を、すべてのモジュールが抽象へ依存するように制御することは、ビジネスロジックを具体的な実装から解き放ち、より純粋なものに昇華する効果があります
この抽象型を用いた依存関係の制御を依存関係逆転の原則といいます。