Hori Blog

フリーランスでバックエンドエンジニアとして活動している Ryota Hori のブログです。
最近はテック系記事より雑記ブログ気味。

DDDの個人的な現状 in 2019

個人的な話となり恐縮ですが、2019 年はドメイン駆動設計の戦術周りでたくさんの試行錯誤ができた有意義な年となりました。

活動していく中で課題感に共感を得られたり方向性の違いを実感できたり学びが多かったので、雑記として残しておこうかと思います。

ドメイン駆動設計#1 Advent Calendar 2019の記事です。

今年やったこと

9 月くらいから休憩中なこともあって generator 芸が未達なのが残念。

DDD のスタンスについて

DDD のスタンスは色々な方向性があり、方向性を共有せずに DDD の各要素の話をすると混乱が生じやすいことを学びました。

前提として私のスタンスを述べておきます。

  • 概念を実装する層では
    • o: 想定する概念をなるべく率直に表現したい
    • x: 実際に使う概念のみ表現する
    • x: 特定の要素(ルールや計算)に特化して実装する
  • DRY (Don’t Repeat Yourself) については
    • o: 変更欲求に対して DRY であることが重要
    • x: なんども使うから共通化
      • o: コードの重複ではなく変更(削除)しやすいほうが重要
  • チームの結合度と概念の結合度について
    • x: 通信を挟むなら(チームも別だし)相互に疎に開発できるよう結合度を下げたい
    • o: API の両端は密結合なのだから概念の結合度に開発を寄せたい
      • 実装上は疎結合っぽくしているけど実質は概念も人間も密結合なのが嫌

色々ありますが、基本的には「概念が主、解決手段が従」に偏重させようとしています。

もしよければ 継続的に価値を提供するドメイン駆動設計入門 も併せて読んでいただけると勘所を押さえられるかと思います。

最近の個人的な DDD

今年は新規立ち上げプロジェクトには恵まれたのですが、プロジェクト継続には恵まれない一年でした。

DDD によるサービス立ち上げを複数行ってみて、個人的に思うことが色々とあったので所感を記しておきます。

概念層以外を手実装するのはかなり厳しい

DDD の実装は二層に大別されます。

  1. 概念層
    • 対象アプリケーションの概念を実装で 表現 する層
  2. 非概念層
    • 対象アプリケーション外との帳尻合わせ、技術的詳細

概念層を概念表現に特化させるため、非概念層は概念層の変更に強く依存することになります。

となると、概念に対し概念層を DRY に保てる一方で、非概念層にも変更が生じるので DRY 原則に反します。

非概念層を実装しなければいけない様子の図

つまり、変更箇所が散り、作業の工数が負担となるため概念と概念層の成長に負の影響が生じてしまいます。

非概念層への作業は非概念層の事情のみに依存し、概念層の変更には(ほぼ)自動で追従して欲しいと実感しました。

非概念層を自動反映にした場合の図

現状の解決策は code generator による依存実装の自動生成ですが、Go 言語との相性の良さもありそれなりに形になってきています。

自分がドメインエキスパートになる気概が必要

体制によると思いますが、最もボトルネックになりがちなのがドメインエキスパートの時間確保でした。加えて、世にないサービスを新しく生むプロジェクトの場合、ドメインエキスパートは部分的なエキスパートでしかない場合も多いのではないでしょうか。

モデリングの初稿はともかく、実装との相互作用による昇華を狙う場合、すべてをドメインエキスパートに確認してから実装するのでは試行錯誤の量が足りませんでした。

待ちが多くて試行錯誤しづらい様子の図

意思決定の分散や認識合わせの不足を招くのは良くないですが、実装の試行錯誤ができ、ドメインエキスパートと揉むポイントを絞れる程度には業務知識を学習しておく気概が必要だと感じました。

仮モデリングによって試行錯誤がしやすくなった様子の図

最終的には調整による二度手間が生じることもありますが、高めた戦術の練度はそのまま活用できるかと思います。

まずはソロ討伐から

これは特に個人的な手応えですが、ドメイン駆動設計の実装で初期に複数人で作業するととても辛かったです。

  • 概念層は概念の変更によってドラスティックに変わる
    • 根本的な設計レベルで柔軟に試行錯誤したいため、共同作業が足枷になりすぎる
    • 共同作業には規約が欲しいが、試行錯誤前に作っても無駄感
  • ドメインエキスパートとの認識共有は超少人数のほうが動きやすい
    • 認識共有する人数が増えるとコストが爆増

スケールデメリットが大きすぎるので、概念層の実装ラインは一本にしたほうがいい、という手応えです。

工数を見積もってウォーターフォール的に分担作業ができるタイミングまでは共同作業にせず、どちらかといえば練兵に努めたほうが最終的にはスピードも増す印象です。

コンテキスト境界を考慮してうまくチーム分割ができる場合や、手数が必要になる非概念層の実装が負担になってきた場合にはスケールメリットが活かせるかと思います。

CQRS や Presentation 層について

DDD の戦術における勘所は想定概念と実装表現を同期させることだと思っていますが、いわゆる「ドメイン」「業務知識」以外の扱いに悩みを持つ人が多い印象です。

特に取得や出力のロジック(Query, Presentation)は「業務知識」とは異なる複雑なロジックによるデータ取得が必要になる場合もあり、業務知識と混在させると辛くなりがちです。

個人的な解釈ですが、「業務知識」といった用語にとらわれず、「欲求」ごとに「概念」をモデリングし、素直に実装で表現することが勘所になると思っています。蓄積されたデータを「取得したい」「表示したい」といった欲求は副作用を生じさせる「業務知識」の欲求とは異なるため、個別に扱うのが自然だと考えています。

  • データ形式が似ているため型定義を使い回しやすい
  • データ取得のロジックも業務知識の RDB に SELECT 文を使用すれば足りがち

といった事情で「業務知識」のリソースを使いまわして逃げ切れることもそれなりに多いですね、くらいの雰囲気で捉えておくのがいいと思います。

更新系と参照系が密結合になるのはわかりやすく辛いので、なんとか工数を確保して結合度を下げていきたい気持ちが強いです。

コンテキスト境界とサービス境界とチーム境界と管理境界とマイクロサービス

基本的にはチームごとの monorepo とし、コンテキスト境界に合わせてディレクトリを切っていくのを好むようになりました。サービス(entrypoint によって起動する何か)はコンテキスト境界と一致せず、各コンテキスト(ディレクトリ)で必要なだけ main 関数を配置しています。

ロジックを持ち、library として依存させるだけで通信せずに活用されるコンテキストもあります(ID Context の新規 ID 発行処理など)。

コンテキストを分け DDD によって開発するのは「凝集度を高め結合度を下げ、概念と実装の好循環を生みたい」という Dev 的な欲求です。Ops 的な欲求におけるマイクロサービスと併せて使う必要は必ずしもなく、それぞれで良し悪しを検討すればいいと思っています。

個人的にはコンテキストごとに起動を分けたほうが素直で楽なのでマイクロサービスっぽくした上で、リポジトリごとにゴリッとデプロイしがちです。マイクロサービスらしくないデプロイフローに見えますが、monorepo をまとめて一つのアプリケーションとして扱っているため、実体としてサービスが分かれているかを気にする必要を感じないのでこうなりました。

Kotlin Multiplatform Projects

Multiplatform Projects - Kotlin Programming Language

プラットフォームや通信の境界に依らずモデルを共有したい場合、実装言語の違いが課題になってきます。

2019 年は Kotlin Multiplatform Projects が活況でした。Kotlin で実装すれば各プラットフォームに対応できるため、モデルを共通化する用途として最も期待しています。

Kotlin Multiplatform 構想 今やる理由編 | AABrain

一方で、現状(2019-12-24 時点)では Kotlin Multiplatform Projects にロックインされる形での開発しかできず、Multiplatform にライブラリ提供をすることは難しいです。成果物は iOS 用の .framework ファイルや、Javascript の実装を出力することができるので Publish さえどうにかなれば…というところです。依存ライブラリの扱いもあるので公開ライブラリとしては課題が多いですが、内部ライブラリとして提供する分には可能性がありそうです(試し中)。

現状は各プラットフォームの開発が Kotlin Multiplatform Projects 内に閉じられる。

現状でのKotlin Multiplatform Projectsの使用感の図

Kotlin Multiplatform Projects で開発したライブラリを各プラットフォームの開発に提供したい。

やりたいKotlin Multiplatform Projectsの使用感の図

おわり

完全に雑記になってしまい申し訳ないですが、今の素直な手応えとして所感を記してみました。

Kotlin Multiplatform Projects や GitHub Package Registry など、管理面で相性の良い仕組みも盛り上がってきており、より一層 DDD の実践が現実的になってきていると感じております。

一般的には DDD の戦術は工数が増えるためスピード感が重要なプロジェクトでは避けられがちな印象がありますが、凝集度を高め結合度を下げる欲求が生まれるのであればプロジェクトの規模に関わらず得られるものが多いと思います。

秋くらいからサービス開発以外に興味が向いているため generator 業が停滞していますが、早く概念表現だけでアプリケーション開発が概ね完結するよう仕上げていきたい所存です。