LoginSignup
64
42

More than 5 years have passed since last update.

ドキュメント指向DBの論理的側面と物理的制約 - DDDと絡めて

Last updated at Posted at 2017-10-02

この記事のモチベーション

RDB(Relational DataBase)の歴史は長く、ノウハウも蓄積されている。
関係代数という学問もあるなどアカデミックにも研究されており、
国家資格である「データベーススペシャリスト」も、ほとんどこのRDBのことを扱うなど、
RDBは一定の体系が確立した技術と言える。

一方で、2010年ごろより広まったNoSQL、特にドキュメント指向DBに関しては、
当然ながらRDBに比べると未成熟な段階であろう。
また主観だが、NoSQLは、スケーラビリティといった物理的側面にfocusが当たることが多かったのではと思う。
一方で、論理的側面 = モデリング に関して、および物理的側面と論理的側面のトレードオフの関係について、
議論されつくされているとは言えない。

本稿では、DDD(ドメイン駆動設計)というプログラミングの論理的側面の手法を、
各DBがいかに扱えるか
ということを主な話題とする。
また、それが物理的側面の制約をどのように受けるか、ということも論じられたらと思う。

RDBの論理的側面(DDDと絡めて)

ER図とDDD

RDBを利用してスキーマを定義するとき、E-R図(E: Entity, R: Relationship)作成は欠かせない。
語弊を恐れず言うと、ここで定義されたEntityと、多対多のRelationshipに対して、テーブルが定義される、という理解だ。
(ER図も粒度があるとのことだが、ここではテーブルを作成するときの粒度のものを思い浮かべてほしい)

さて、ではここでいうEntityと、DDDでいうEntityは、一致しているのだろうか
これは個々の解釈に依存するので、深入りしたくない領域でもあるが、最低限、定義を確認する。

  • DDDで定義されたEntityとは、identityによって区別される必要のあるモデルのことだ。
  • ER図のEntityは属性をもったデータの集まりと解される事が多い。

DDDでは「Model」と呼ばれているものが、ER図のEntityに相当すると思っても良い。
つまり、ER図のEntityにはDDDでいうValue Objectも含む(としてER図を書く人もいる)のだ

特に昨今隆盛を極める、ORマッパーつきのeasyなWeb Frameworkを用いると、
Value ObjectやLocal Entityといった概念スキーマの深淵に深く入らずとも、
永続化するべきデータの集まりであれば、とりあえずActiveRecordなどで管理できてしまう。
概念レベルでは存在しないglobalなidを付与されるが、それはRDBの要求である。

構造化の方向性の違い

語り尽くされているが、ObjectとRelationのインピーダンスミスマッチは、DDDとRDBの垣根をつくる。
集約, 階層化という縦方向の広がりをもったDDDに対し、テーブル、属性という横方向に関心のあるRDB。

RDBの論理モデリングは、DDD内の概念モデリングがそのまま引き継がれないことも多い。
DDDでは、Repositoryという概念で、RDBという「外界」からデータをとってくるという位置づけにしている。

本題: ドキュメント指向DBとDDD

ドキュメント指向DBと集約

ドキュメント指向DBは、ざっくり言ってしまえば、階層化、縦方向に関心のあるDBだ。
テーブルという、データ格納の効率性を犠牲にして、スキーマをなくした。

このドキュメント指向DBに、そっくりそのままDDDでいう「集約ルート」を格納することを考える。
集約ルートは、外部から参照されうる集約の代表で、集約とライフサイクルをともにしたEntityである。
集約ルートの配下に、集約に所属するLocal EntityやValue Objectを含めることで、

  • 集約のライフサイクル === 集約ルートのライフサイクル
  • 集約ルートのみが外部から参照されうる
  • 集約に閉じた処理のトランザクション性 (1ドキュメント内の処理はAtomicになるので)

といった性質が満たされる。
ドキュメント指向DBは、集約を軸にしたDDDモデリングと相性がいいといえる。

ドキュメント指向DBとRepository / Factory

DB to memory

そうはいってもDBはプログラミング言語の扱うメモリ領域からすれば、外界だ。
結局はDB内に構造化された集約のデータを、適切にメモリに載せる必要がある。
JavaScriptなどJSONからモデルへの変換がほぼシームレスに行える言語であれば、その心配はない。
せいぜいconstructorに処理を書く程度だ。あとはそのままDDDの世界のコードを実行すればよい。
静的型付けの言語も、型変換のライブラリを利用するなどがORマッパー的といえるかもしれないが、
RDBよりも直感的なマッピングが可能である。

memory to DB

DBにstate sourcingする場合、集約ルートをJSON化するメソッドさえ定義しておけばよい。
JavaScriptではその必要さえなく、モデルをそのまま送信してもよき解釈をされる。

event sourcingをしたい場合は、差分処理(Update Operators)をそのまま保存することになる。

無限に増えるデータ問題

しかしこれは、論理に閉じた場合の話であり、ドキュメント指向もまた物理的制約を受ける。
集約内のデータが無限に増える時の話である。

例えばヘルスケアアプリ等における無限に測定可能な体重や血圧などのデータは、
ユーザーという集約ルート内のValue Objectだ(Local Entityだ、という人もいるが、ここでは議論しない)。
これを、ドキュメント指向DBのユーザー配下に格納することを考える。

そうすると、1ドキュメントのサイズ制限に達するリスクがある。
Mongoは16MB、DynamoDBだと400KBという制限がある。

この制限のために、集約をそのままモデリングすることが躊躇されるかもしれない。
実際には、日々のデータサイズを見積もることで、格納が可能という結論に達したりもするので、
モデルの数量的見積もりが判断に重要になる。

処理側との通信量

たかだか400KBのデータであれば、1つの集約を通信によって取得/処理するとよい。
JavaScriptなどJSONからモデルへの変換がほぼシームレスに行える言語であれば、そのままDDDの世界のコードを実行すればよい。

問題は、16MBのデータだ。この場合、1つの集約をまるまるクライアントに落とす処理を毎回行うのは効率が良くない。
SELECT FIELD_A FROM TABLEのように、mongodbもprojectionというしくみで、特定のfieldを取得しないようにできる。
ただこの場合、モデリング側がLocal Entity/Value Objectがある前提でモデリングしているため、Null参照エラーとなってしまう
もちろんその結果をnullableなtypeとして表現することはできるが、それは物理的制約が論理モデルに漏れ出ている状態である。
ViewModelとか、概念モデルよりもさらに実践的なレイヤが、概念スキーマをすっとばしてDBの好きな値をより好みするということになると、
概念モデリングの本質をそのまま享受できない。
(≒たぶんGraphQLに対して私の抱く違和感はこれだ)

Event Sourcing / 状態同期の可能性

上記の問題の解決策は、個別の問題に限れば、ないわけではない。
例えば、クライアントが関心を持つ集約が限られている場合に、そのクライアントが内部でストレージを持つことを考える。
クライアントは、DBのデータそのものでなく、DBの差分を取得し、それを反映するかたちでデータを管理する。
そうすると通信量は少ないまま、集約をすべて手元に持っておくことができるだろう。ロバスト性やpush型通信などに課題はあるが、
クライアント主権時代、関心のある集約が少ない通信量で同期される世界が実現できれば、
プログラマーの仕事は、概念スキーマに集中することだけになる。

まとめ

ドキュメント指向DBのモデリングは、よりDDDにとって直感的なものであるといえる。
一方で、決して無視できない物理的制約を、導入前に評価することが求められる。
評価はドメイン知識、サービスの運用イメージが必要となる。
その大枠が決まれば、あとはソフトウェア開発者の仕事は、利用側の概念スキーマに集中することとなるだろう。

参考文献

64
42
1

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
64
42