0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

DDDの集約について考える

Last updated at Posted at 2022-08-10

はじめに

集約がまじでわからん。
ということで、自分の理解のために考えてみた。
注:間違っている可能性が高いです

集約の意義

サークルにはメンバーが加入でき、加入できる人数は最大10人まで、みたいなルールがあった場合に
エンティティとしてはサークル has many メンバーみたいな感じ
で、サークルにメンバーを加入するロジックとしては

circle.members.add(user)

ただこれだと、利用側で人数に関するバリデーションをかけなければならず、

if circle.members.count > 10 {
  "人数が上限を上回っています"
}

同じロジックが複数箇所に書かれてしまう。
なので、サークルを集約ルートとして、人数に関するルールはサークルに実装することで、ロジックの散在を防ぐことができる。

circle.join(user)

と、この辺までは教科書的な説明で簡単。
難しいのは、repositoryとかが絡んできたとき。

集約とrepositoryについて深堀

あるサークルにメンバーを追加したいとする。
テーブル的にはclrcle_membersにレコードを作成することになる。
(circle_idとuser_idをもっている)
この場合、circle_idとuser_idを受け取ってcircleMemberRepsitoryに渡すべきか?
ただそうすると、先程の人数に関するルールが守られない可能性がある。

circle_member = new CircleMember(circle_id, user_id)
circleMemberRepository.save(circle_member) # 10人以上保存できてしまう

常にルールを守らせるようにするには、circleを通して変更する必要がある。
では、どうするか?

circle = circleRepository.find(circle_id)
member = userRepository.find(user_id)
circle.join(member)
circleRepository.save(circle)

つまり、circleRepositoryで紐づくmemberも保存すべし、となる。
まとめると、repositoryを集約単位で作ることで、集約に関するルールを常に守らせることができる。

別の例。
ユーザが複数のメールアドレスを登録できるが、最大3つまで、みたいなルールがあったとする。
この場合の集約ルートはユーザということになる。
テーブル構成的にユーザテーブルに3つのカラムとしてアドレスを含めることもできるが、
アドレスを登録するタイミングがユーザ作成のタイミングと異なり1個1個登録する場合は
ユーザメールアドレスというテーブルを作ることも考えられる。
今回はそのケースで考える。
先程の教訓から行くと、メールアドレスを追加したい場合はuserRepositoryを通して行う、ということになる。

user = userRepository.find(user_id)
user.addEmail(address)
userRepository.save(user)

この場合、userRepository.findで紐づくメールアドレスも取得し、
userRepository.saveで紐づくメールアドレスも保存する、となる。

紐づくメールアドレスも保存する、の部分について詳しく。
上記の場合であれば、追加されたメールアドレスだけ保存すればよいが、
UI上複数のメールアドレスを一括で登録したい、みたいな要件があった場合。
(常にユーザが入力したメールアドレスのセットを正として洗い替えを行うとする)

user = userRepository.find(user_id)
user.replaceEmails(addresses)
userRepository.save(user)

この場合は、userRepository.save(user)の中で紐づくメールアドレスのdelete-insertを行う。

別のユースケースで、単純にユーザの更新のみを行いたい場合。

user = userRepository.find(user_id)
user.name = "hoge"
userRepository.save(user)

この場合、userRepository.find(user_id)の時点で紐づくメールアドレスも取得しているので、
userRepository.save(user)で無駄なアップデート処理が走るが、実害はない。
もしもuserRepository.find(user_id)で紐づくメールアドレスを取ってこなかったとすると、
userRepository.save(user)の際に元々存在していたメールアドレスを消すことになってしまう。

思ったこと

repositoryは集約単位で作る。
その場合は、必ず生成と保存を集約単位で行う必要がある。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?