Hori Blog

フリーランスでバックエンドエンジニアとして活動している Ryota Hori のブログです。ドメイン駆動設計や Go の話など。

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業が停滞していますが、早く概念表現だけでアプリケーション開発が概ね完結するよう仕上げていきたい所存です。