りゅうじの学習blog

学習したことをアウトプットしていきます。

5/16 ドメイン駆動設計Chapter5 テスト前まで

リポジトリとは何か

データの保管庫。

ドメインオブジェクト(値オブジェクト・エンティティ)とデータベースを直接やりとりせず、リポジトリを介して行う。

リポジトリのインターフェース

定義するものは、リポジトリの責務である、オブジェクトの永続化に関わるものだけです。

interface IUserRepository {
  save(user: User): void;
  find(name: UserName): User;
}
  • インターフェース(抽象型)なので実体は別のクラスで定義します
  • saveメソッドとfindメソッドがあることがわかります。
  • Chapter4で使ったexistsメソッド(重複チェック)はリポジトリに定義するべきか?
    • 重複チェックは責務であるオブジェクトの永続化ではないので定義せずドメインサービスに定義する
      • ドメインサービスにインフラにまつわる処理を書きたくない場合は、リポジトリに求田的な重複確認のキーを引き渡すと良い
interface IUserRepository {
  save(user: User): void;
  find(name: UserName): User;
  exists(name: UserName): boolean;
}
  • ユーザ名で重複確認をするのであれば、Userを渡すのではなく、具体的なUserNameを引き渡す(ドメインサービスにインフラにまつわる処理を書きたくない場合)
    • Userを渡してしまうとドメインサービスが主体とならなくなる
      • ドメインサービスのコードに重複確認にであるUserNameが書かれず、Userとなってしまうからである

まとめ

重複チェックメソッドの定義において、ドメインサービスにインフラにまつわる処理を書きたくないから、リポジトリに定義する場合。

ダメな例

interface IUserRepository {
  save(user: User): void;
  find(name: UserName): User;
  // 引数に具体的なキーのユーザ名ではなくUserとしている
  exists(name: User): boolean;
}

class UserService {
  constructor(private readonly user: User, private readonly userRepository: IUserRepository) {}
  public exists(user: User): boolean {
   // ドメインサービスがユーザ名により重複確認するという知識が失われている
    return this.userRepository.exists(this.user);
  }
}

良い例

interface IUserRepository {
  save(user: User): void;
  find(name: UserName): User;
  // 重複チェックの具体的なキーであるユーザ名UserNameを引数としている
  exists(name: UserName): boolean;
}

class UserService {
  constructor(private readonly userName: UserName, private readonly userRepository: IUserRepository) {}
  public exists(user: User): boolean {
    // ドメインサービスでユーザ名で重複チェックを行うのだと見ればわかる
    return this.userRepository.exists(this.userName);
  }
}

リポジトリの実体のクラスを作成

インターフェース(抽象型)で定義した実体です(existsは省きます)

例としてORMはPrismaを使ったと仮定したもの(Prismaのimport分等省きます)

//インターフェース
interface IUserRepository {
  save(user: User): void;
  find(name: UserName): User;
}

//ここからリポジトリの実体
class UserRepository implements IUserRepository {
  private prisma: PrismaClient;

  constructor(prisma: PrismaClient) {
    this.prisma = prisma;
  }

  async save(user: User): Promise<void> {
    const { id, name } = user;
    await this.prisma.user.create({
      data: {
        id: id,
        name: name,
      },
    });
  }

  async find(name: UserName): Promise<User | null> {
    const user = await this.prisma.user.findUnique({
      where: {
        name: name,
      },
    });
    return user ? new User(user.id, user.name) : null;
  }
}
  • saveメソッドとfindメソッドの内容がここで実装されています
    • implementsでIUserRepositoryを部分型としているのでsave・findメソッドが定義されていないとコンパイルエラーになる

参考

ドメイン駆動設計入門 ボトムアップでわかる!ドメイン駆動設計の基本