編集可能

ドメイン駆動設計と相互依存性

ドメイン駆動設計でグローバルなエンティティ同士の依存性について
9
こっつん @cttsn

しかしちゃんとドメイン駆動設計すると関連テーブルが使いづらい…

2014-03-19 00:45:44
かとじゅん @j5ik2o

@pnkts う?相談のりましょうか?

2014-03-19 11:52:11
こっつん @cttsn

@j5ik2o https://t.co/CM8DigG9qZ かなり参考にしてます! 例えばユーザは複数グループに所属でき、ユーザの所属一覧とグループのユーザ一覧と両方欲しい場合、所属エンティティの属性にユーザとグループ内の値オブジェクトをコピーすればいい感じですかね…

2014-03-19 13:35:51
かとじゅん @j5ik2o

@pnkts あ、だいたいわかります。ユーザはグループではないのでグループを表す属性は持ちませんし、その反対も同様です。なので、ユーザには所属する複数のグループへの参照を持つか、グループが所属される複数のユーザへの参照を保持するかという話になりますね、たぶん(続く

2014-03-19 13:49:39
かとじゅん @j5ik2o

@pnkts まず相互依存の関連は維持管理がシビアなので最初から前提にしないで一方向になるようにしましょう。つまり便利な関連は排除し本質的な関連だけにする。仮にユーザにグループへの参照があるとした場合で説明すると。

2014-03-19 13:54:05
かとじゅん @j5ik2o

@pnkts ユーザとグループはそれぞれ独立したグローバルな識別子を持つエンティティであり、集約です。ユーザには所属するグループへの参照としてグループIDの集合を属性に持ちます。このグループIDの値はユーザの値です。なので集約内の属性としてIOすれば言い訳です。

2014-03-19 13:56:58
こっつん @cttsn

@j5ik2o 識別子自体は参照ということなので集約間で共有できるわけですね。モヤモヤしていたところが解消されました!ありがとうございます!

2014-03-19 14:02:00
かとじゅん @j5ik2o

@pnkts あ、前提としてユーザとグループは独立した識別される概念なのでエンティティとしています。

2014-03-19 13:59:02
かとじゅん @j5ik2o

.@pnkts 言語上の参照で保持しちゃうと集約内部か外部かが見分けがつかないので基本識別子を使うしそれはランタイム表現なんですよ。集約であるエンティティはランタイム上のライフサイクルを超えうるのでたとえばストレージなどに存在する場合は表現できなくなります。なのでIDがよいです。

2014-03-19 14:06:56
こっつん @cttsn

@j5ik2o ユーザが値オブジェクトだったら匿名性バッチリですね! つまり所属というエンティティを新たに作るとユーザとグループの境界が曖昧になり不味いので、どちらかに片方の参照を集約しておくということですかね。

2014-03-19 14:11:20
かとじゅん @j5ik2o

@pnkts ユーザは値オブジェクトになりますか?見分ける対象の場合はエンティティにしないとだめですよ。 http://t.co/8ZKZiFGmoi

2014-03-19 14:55:41
こっつん @cttsn

グループのテーブルの行にユーザID列をシリアライズして格納するのは嫌なのでlazyloadingで参照をIOすればいいのか

2014-03-19 14:30:23
かとじゅん @j5ik2o

@pnkts 実装から考えるのではなく、ドメイン層というのは概念から考えないとダメなんですよね。ユーザが値オブジェクトかエンティティかはどういう概念のものを扱おうとしているか最初に整理しないといけませんね。実装都合で、やーこれはVOだな、Eだなというものではないのでw

2014-03-19 14:57:57
こっつん @cttsn

@j5ik2o たしかに順序がありますね。モデルと設計は一致させなければならないのに早々と実装に振り回されると釣り合いが保たれないです。

2014-03-19 15:26:32
かとじゅん @j5ik2o

@pnkts 仮にユーザがVOの場合は見分けが付かなくなります。ユーザを認証するようなアプリケーションの場合はそれぞれのユーザを識別するわけですが、VOだとこれが不可能になりますね。最初にユーザとは何か?とかどういう使われ方をするモデルなのかを定義しないといけませんね。

2014-03-19 14:59:50
こっつん @cttsn

@j5ik2o ユーザのように明らかに要識別の物はわかりやすいですが、今までRDBMの機能に縋ってた身としては関連テーブルまでもEと錯覚してしまいがちです。

2014-03-19 15:32:13
かとじゅん @j5ik2o

@pnkts みんないい感じにERD脳にチューニングされているので一旦忘れて考えた方がいいです。DDDにおいては。

2014-03-19 15:33:49
かとじゅん @j5ik2o

@pnkts class User(id: UserId, groupId: GroupId, name: String); class Group(id: GroupId, name: String);

2014-03-19 15:08:43
かとじゅん @j5ik2o

@pnkts UserRepository#findById(uid): User; UserRepository#findByGroupId(gid): Seq[User]; GroupRepository#findById(gid);

2014-03-19 15:10:13
こっつん @cttsn

@j5ik2o 1対多ならgroupIds: Seq[GroupId]とすればいいですかね

2014-03-19 15:34:53
かとじゅん @j5ik2o

@pnkts そそ。class User(id: UserId, groupIds: Seq[GroupId], ...)

2014-03-19 15:35:32
かとじゅん @j5ik2o

@pnkts その属性がいい感じにRDBMSのテーブルにマッピングされるように考えればいいですね。UserRepository#findByGroupIdを可能にするならばインデックスを効かせたいのでUSERテーブルとUSER_GROUPIDSテーブルみたいな感じにするかな。

2014-03-19 15:38:03
こっつん @cttsn

@j5ik2o かなりすっきりしてきたと思います。集約のルートのみにしかリポジトリが存在しないことでクライアント側から好き勝手アクセスして混沌化することを防げるのも納得してきました

2014-03-19 15:45:22
かとじゅん @j5ik2o

@pnkts 概念があって実装クラスって感じなので、最初の概念がデカいと集約自体も実装がデカくなるので、I/Oもボトルネックになりやすいと考えるとよいですー。実は二つのエンティティが隠れているのでは?ということで考え直すことで性能もよくなったりします。

2014-03-19 15:48:54
こっつん @cttsn

@j5ik2o ありがとうございます。リポジトリ層で完結できればクエリのチューニングで対応してそもそも規模が大きすぎるならモデルの見直しを意識していこうと思います。

2014-03-19 15:55:12

コメント

かとじゅん @j5ik2o 2014年3月19日
@pnkts ユーザは値オブジェクトになりますか?見分ける対象の場合はエンティティにしないとだめですよ。
0
こっつん @cttsn 2014年3月19日
まとめを更新しました。
0