読者です 読者をやめる 読者になる 読者になる

DDDと高負荷サービス(というか I/Oアクセス)は相性が悪いのか?

DDDはActiveRecordパターンのように1つのモデルが1つのテーブルと紐づくとは限らない。

具体例を出すと、
DBにUserテーブルがあるからといってUserモデルを作るとは限らない。

ログインユーザーを表現するLoginUserモデルと
ユーザーのプロフィールを表現するUserProfileモデルが作られるかもしれない。

DDDはあくまでもドメインを中心に考える必要があるので、
基本的にDBのデータ構造とドメインモデルのデータ構造は別物として考え、
ドメイン中心で考える。

ただ、そうなるとI/Oアクセスが多くなる可能性がある。

例えば、上記の LoginUser, UserProfile が集約だとすると、それぞれの Repository が作られる。
以下のコードの LoginUserRepository, UserProfileRepository は内部的にUserテーブルにアクセスしているので、
合計2回アクセスしていることになる。

loginUser = LoginUserRepository.get(); // Userテーブルにアクセス
userProfile = UserProfileRepository.get(); // こっちでもUserテーブルにアクセス

これがActiveRecordパターンであれば、以下のように1回のアクセスで済む。
もちろん、I/O回数が1回の代わりに LoginUser, UserProfile といったドメイン中心のモデリングはできなくなる。

user = User.find()

ソーシャルゲームとかの高負荷サービスだとI/Oがボトルネックになる傾向にあるので、
こういった無駄なI/Oは可能な限り減らしたい(DDD的には無駄ではないのは十分承知しているが・・・)。

もちろん、memcacheのようなキャッシュを利用できるかもしれないし、
MySQLであればクエリキャッシュ効くかもしれないし、
オンメモリでキャッシュとかできるかもしれない。

個人的には上記の例のように同じテーブルに2回、3回アクセスするケースは多くないと思うので、
これがボトルネックになる可能性は低いと思っている。
(何の根拠もないから、ほぼ勘みたいな感じだけど・・・)

ただ、作ってみないと分からないし、
負荷ってインフラコストに直結する可能性あるから低いに越したことないし(スレーブ2台より1台の方がいいでしょ)、
サービス運用してみないと分からないので(ある程度はベンチで予測できるけど)、
何も考えないというのは違う気がする。

結論をいうと、
個人的には高負荷になりそうな部分はモデリングを崩してもいいと思っている。
もちろん、ちゃんとベンチ取ったり、仕様を変更したり、ドメインの質を保てるような努力はするけど、
さすがに動かないサービスに価値はないので、ここは仕方ないかなと・・・。

いろいろ考えてみたけど、
こういったI/Oがボトルネックになるケースがどの程度あるのかね・・・?
まずはベンチ取るところからかな・・・。

ちなみに、以下のように
ActiveRecord である User から LoginUser, UserProfile を生成することも考えてます。

user = User.find() // ここでUserテーブルにアクセスするだけ
loginUser = user.createLoginUser(); // これは factory
userProfile = user.createUserProfile(); // こっちも factory

これだとI/O問題クリアできるけど、
「このUser って何?」 ってなるし、
User と LoginUser, UserProfile の関連性というか、
なんかそこら辺を上手く整理できてないから、
この実装はちょっと考え中。
というか、直感的にNGなパターンな気がしてる・・・。

DDDってドメインモデルをどうモデリングするかってのがコアな部分なのに、
こーゆー技術的なハードルに引っかかるんだよな・・・・。