DDD開眼した

新しいパラダイムを自身にインストールするときは、なんであれ少なからずブレイクスルーのような瞬間があると思うけど、今回はかなりのアハ体験を得たので拙いながらも書き記しておこうと思う。

まず読んだ本を雑にまとめたうえで、LL脳な自分が感じた壁とそれがどう取り除かれたかを書き散らかしていく。完全に自分向けのメモなので例とかコードとか置いてないけど、少しでも雰囲気が残せたら嬉しい。
なおご多分に漏れず肥大化したモノリスコードベースの分割したさがこれらのモチベーションの源泉で、モジュール化のアプローチと真逆に位置するRoRでなんとか責務を分解しようとした七転八倒*1の結果得た開眼なので、正攻法な理解ではないかも。

読んだ本

クリーンアーキテクチャ

Clean Architecture 達人に学ぶソフトウェアの構造と設計【委託】 - 達人出版会

前半はSOLID原則とデザインパターンのおさらい、後半はモジュール化とレイヤードアーキテクチャで責務を分割してっょぃアプリケーションを作ろうな!という本(雑)。
責務を分割するというのはどういうことなのか、インターフェイスとはなにか(その利点はなにか)、モジュール化による旨味はなにか、の答えがそれぞれ書いてある。

実践DDD

実践ドメイン駆動設計(ヴォーン・ヴァーノン 髙木 正弘)|翔泳社の本

いきなりエヴァンス本を読むと挫折するだろうとしてこちらを読み始めたものの、エヴァンス本で出たワードの解説は特にないので結局路頭に迷う。読了後なにもかもわからんが!?となったあなた、それは間違いではありません。
概念の説明と各論すぎる各論(Hibernateの使い方とか…)が入り混じっている・章どうしの循環参照が激しいせいで大変読みにくい。本書のリファクタリングを要求します。

とまあ、愚痴は置いといて、ひとまず1周流し読みする→インターネットで情報を補強しつつ試し書きする→もう1周読む、を繰り返すことで開眼に至った。

なお「ドメイン駆動設計 モデリング/実装ガイド」を間に挟むと開眼までのサイクルが早くなる。実践・実践DDD本といった内容。大変お世話になりました…。
ドメイン駆動設計 モデリング/実装ガイド - little-hands - BOOTH

モノリスからマイクロサービスへ

O'Reilly Japan - モノリスからマイクロサービスへ

モノリスに境界をもたせるのにDDDの集約やコンテキストを使うとよい、あくまでも分離のためにDDDのエッセンスを適用しよう、といったことが書いてあった。
境界を炙り出すのにイベントストーミングをやってみるというのは、今までは違う観点で機能やビジネスロジックを捉えることができてよいアプローチだった*2
一周してみて、一度アクセルを踏み切らないと(DDDをひととおり試してみないと)「エッセンスの適用」は難しいよと思っているところ。

壁たち

インターフェイスとモジュール

インターフェイスとは無縁の人生だった*3のでこの概念を取り入れるのに結構苦労した。「依存関係が逆転する」の意味と、main関数は究極の詳細であるということが腑に落ちたタイミングで分かった気になり、社のテックブログに寄稿した2エントリでの経験を経て乗り越えた。

Pure Kotlin+gRPC(protobuf)なWebアプリを作るまで - ロコガイド テックブログ
ServerSide Kotlin Appをマルチモジュール化する - ロコガイド テックブログ

当たり前すぎるけど、やはりインターフェイスとモジュールの概念のある言語で一通り書いてみるというのが大事。

DDDの戦略と戦術

実践DDD本の2-3章を読んでも戦略DDDとはなんぞやというのが分からないと思うけど、ざっくり理解をまとめると、「戦略」は設計思想なので、要件に対して責務を組み立て(ないしは分解)していく過程で使うといいという話だった。
LL脳は要件が与えられるとついついデータモデルから考え始めてしまうけど、これを押し留めて世界観を描くところからやると違ったアプローチが取れる。CQRSなんかが「違ったアプローチ」のいい例。
一方から入稿されたデータを他方で加工して閲覧させるようなユースケースの場合、このCQRS的なアプローチがとても有効なように思う。
「戦術」は立ち位置としてはクリーンアーキテクチャ(の実践)寄りというか、実際のコードをどういう粒度でモジュール化/カプセル化するかという話。前提(思想)がついてこないと片手落ちというのも頷ける。

クリーンアーキテクチャとDDDの折り合い

どちらも微妙に同じワードが出てくるからたちがわるい(エンティティとかサービスとか)。あとDDDでサービスが2種類出てくる(アプリケーションサービスとドメインサービス)のも厄介。「ドメイン駆動設計 モデリング/実装ガイド」にはこの混乱を避けるためにアプリケーション(サービス)層を「ユースケース層」として命名せよと書いてあった。頭いい…。
クリーンアーキテクチャでもDDDでもレイヤードアーキテクチャをベースに語られているけど、本当にDDDを実践するのならオニオン的アーキテクチャのほうがいいっぽい。
なおプロダクションコードでクリーンアーキテクチャをベースにDDDのエッセンスを少しずつ取り入れていってみたら、最終的に完全にDDDに飲み込まれた。中途半端な援用は混乱を招く。

エンティティと値オブジェクト

ここが最後の難関だった。端的に言うと「データストアから引いてきたデータをそのままエンティティにするのは間違い」ということなんだけど…。
正規化・非正規化のプロセスが挟まったとしても、データストアにあるデータをエンティティとしてマッピングする必要はなくて、エンティティと取るか値オブジェクト(プレーンなクラス)と取るかは「そのコンテキスト/ユースケースの中で状態を遷移させる必要があるか」で決めたらいいということ。そしてエンティティに紐付いたプロパティ(としての値オブジェクト)は、永続化に必要なデータ以外も持ちうるということ。
つまりユースケースの中で「エンティティを組み立てるのに必要な情報」と「エンティティを永続化するのに必要な情報」は必ずしも一致するわけではないと。これが腑に落ちるとドメインオブジェクトの表現力が格段に上がるなと実感した。
そしてここにたどり着くまでに随分時間がかかったことで「データモデル第一主義脳」の呪いが思いの外強いことにも気づくなどした。こわいね!

*1:この話ももう少しまとまったら記しておきたい

*2:なおこのアプローチはドメインエキスパートらとワイワイやることが推奨されているけど、いろいろあって1人でやりました 寂しい!

*3:golangがっつり書いたのもisuconくらいなもんで