🤔

「DDD をやっている」とは

2021/02/16に公開

脳内整理のため、つまり僕のためのポエム

自分の中で理解が一区切りついた感じがしたので、コミットしておく

「DDD をやっている」に対して感じていた違和感

DDD を始め ( させられ ) て以降感じていた違和感がある

僕にはこんなフレーズが頻繁に聞こえていた
若干文面は違うだろうが、弊社に限らないニュアンスだとは思う

これらに対するモヤっとを解消したいのでいろいろ考え直してみた

曰く「DDD だと仕様とモデルが一致する」

第一印象

  • いや、テキストと絵は一致しないっしょ?
  • そもそも一致ってなにさ?
    • 仕様書を Java で書くってこと?
    • それとも実装を日本語でするってこと?

曰く「DDD だと業務の文章でコーディングする」

第一印象

  • 業務の〜もなにも文章は全部日本語じゃん?
  • そもそも、じゃあ DDD でなければ何の文章で実装していると仰るの?

曰く「DDD だと変更に強い」

第一印象

  • DDD だからなの?
  • 例えばテストコードの有無は?
  • そもそも変更って何?
  • あと「強い」ってなに?

曰く「DDD は Value Object とレイヤー設計により堅牢」

第一印象

  • DDD 独自のことなの?
  • これをやれば DDD なの?
  • 例えばプリミティブな型を使わなければ堅牢なの?
  • そもそも堅牢って何?

5 年くらい考えて思い至ったこと

淡々と箇条書き

この段落は違和感の箇条書きと順番はリンクしてません

モデル

作り方

  • 仕様書から単語を抽出する
  • 名詞だけではだめで動詞が必要
    • 名詞が箱で動詞が線

モデルは関連を整理した単語帳

  • 目的は単語をはっきりさせることと、関連を整理すること
  • モデルでコードが書けるわけではない
  • クラス図を描きがちだけど、クラス図ではない
  • モデルでコードは書けないけど、全ての成果物はモデルから作る
  • 成果物を更新するたびにモデルも更新する
    • 例えば ICONIX とかやるとマジでそうさせられる

単語

適当に今でっち上げた仕様書の一文を例に、いろいろ考える

「ガスのプランとセット割特典に応じて割引をメールする」

品詞

  • 名詞には振る舞いが必要で、動詞には操作する物が必要
  • かなり深く気をつけてないと結構穴だらけだったりする
    • 割引とかメールするみたいに品詞をはっきりさせないで使ってると怪しい
    • 割引額なら名詞で割引するなら動詞だけど、どちらかでは文章は成立しない
    • メールするもちゃんとメール送信するとする
  • あと大体、体言止めは怪しい
  • 「しゃけ」「おかか」では会話できない
    • なので名詞と動詞が正しく必要

コンテキスト

  • 名詞を正しく認識する必要がある
  • 契約か定義か実績か、みたいな境界をマジでがっちりはっきりさせる
  • 「ガス」って何さ
    • 「2020/10/25 から A さんが契約しているもの」という契約か
    • 「月々 xxx 円で yyy 分量のガスが使えるもの」という定義か
    • 「2020/10 の A さんの利用量」という実績か
  • 「ガス」って気体だからね?マジで気体の話してるわけではないでしょ? ということ
  • ここを曖昧にした結果は、須らく悲惨
    • Gas.javaに全部が曖昧に入っていて全ての仕様変更は結局ここに影響する、みたいになる

単語と文章の違い

  • 繰り返すが、単語では会話できないしそれっぽい文章でもコードは書けない
  • モデルにある単語を使っている文章だからおk、なんてレベルでは甘すぎる全然だめ
    • 単語の羅列止まり -> 「ガスのプランとセット割特典に応じて割引をメールする」
    • ユースケース記述 -> 「先月のガス利用契約プランとセット割特典に応じて、ガス利用割引額を決定し割引する。割引後にガス利用割引額を記載したメールを割引対象者のアドレスに送信する」
  • ここまでしてやっと、文章からクラス図が見えてくるレベルになっている

変更

種類と対応方法

  • 変更と言ってもいろいろある
    1. サービススペックの変更
    2. サブサービスの増減
    3. フレームワークの変更
    4. インフラの変更
    5. 優先する機能の変更
  • 1, 2 はモデルで整理
  • 3, 4 はドメインの外の変更がメイン
  • 5 はどちらかというと開発プロセスで吸収すること
  • DDD だからって変更時に思考停止でドメインだけ見てもだめ
  • xx 駆動開発は xx を中心にというニュアンスであり、別にそれ以外を軽視しない

タイミング

  • 種類と同じくタイミングにもいくつかある
    • ファーストリリースを早くしたい
    • どんどん機能追加したいからリリース後頻繁に変更したい
    • 多くはないけど無停止で対応したい
  • いつどれくらい投資して、いつの変更を、どう対処したいかを考えておく

全てに完璧に備えない

  • というか無理、エヴァンスも言ってたはず
  • 「変更が影響する領域と程度をコントロールする判断」を設計というと最近は考えている
  • そういう判断の先もしくは基準に、コアドメインがあるはず

設計

材料

  • 先述の通り、変更をどうしたいかの判断が設計
  • 変更や影響を想定する材料はコーディング知識だけではない
    • 会社の中期計画
    • 他社動向
    • 法令
    • インフラ老朽化
    • 各種フレームワークの EoL
    • チームの力量

残す

  • 大体の場合、決定した形以外は残りづらい
    • 議論して採用しなかったものがなぜ採用されなかったか
    • なぜ今これを採用しているのか
  • なので Architecture Decision Records を書いていきたい

共通化

  • 処理が同じところを共通化してもだめ
  • 業務が同じところを抽象化する
  • 業務が同じなのもたまたまかもしれないので、業務を定義しておく
  • 同じ都合で同じ変更をする箇所を同じコードにすると考える

Value Object ( VO )

  • Intでは辛いので金額にする」のはただの安直なプリミティブラッパー止まり
  • 金額の意味と許される演算を考えて明細料金 合計料金 割引額になると初めて意味を持つ
  • 同様にList[Item]PurchasedItemsでは意味と制約と許される演算が違う
    • List[Item]商品一覧在庫か曖昧だが、PurchasedItemsは限定的
    • List[Item]では許される内容数が不明だが、PurchasedItemsは制約を書ける
    • List[Item]Listにできる全演算ができるが、PurchasedItemsfilterできない
  • これは DDD に限り使われる手法ではない、普遍的な技の一つ

レイヤー設計

  • Domain Layer を外の Layer に依存させない
  • DDD だから Domain Layer を末端にしているのではない
  • Domain Layer を業務変更以外で触らないために末端にしている
  • X Layer から Y Layer を触って良いだの悪いだの、それも DDD の決まりではなくて理由がある
  • Clean Architecture とかを読むと、依存の制御は普遍的な技だとわかる
    • Dependency Injection Principle のただの一例
  • 巷にあるそれを保証する仕組みを DDD と混ぜない
    • gradle や sbt のプロジェクトを分けるだとか
    • check style で import を監視するとか

議論

ちょっと筋違いだけどちょいちょい感じるので

力量の違いと流派の違い

  • 全てを対等に扱うと破綻する
  • 例えば守破離の 破 をベースラインとしている場合
    • 守 vs 離 は指摘や指導にあたる
    • 離 vs 離 は判断の違いという議論にあたる
  • 議論の表面的な決定だけを持って来て使ってもだめ
    • 離 に達していない
      • ex) 「テスト書かなくてもいいですか」
      • 「やらない選択肢があるんだな」とはならない、基本的に「だめです」
      • 基本的な答えがある
    • 流派や現場の違い
      • ex) 「DomainService から Repository を呼んで良いか」
      • 「うちでは認めていない」けど「呼ばない方が良い面が多い」という感じ
      • 正解がないので判断する
  • 「オフサイドとは」と「ディフェンスは何人であるべきか」って違いだとイメージしやすいか[1]

解決編

散漫な知見をまとめるきっかけになったのが ICONIX と Zenn のスクラップ機能

年末年始くらいにまとめてた -> ICONIX で Re:DDD と FP

「正面から違和感と向き合うかー」と「積みっぱなしだった ICONIX 学ぶかー」がちょうど重なったので、そういう意味でも自分の中で一区切りだった

曰く「DDD だと仕様とモデルが一致する」

第一印象

  • いや、テキストと絵は一致しないっしょ?
  • そもそも一致ってなにさ?
    • 仕様書を Java で書くってこと?
    • それとも実装を日本語でするってこと?

今の考え

  • モデルはあくまで関連のある単語帳だ
  • 今後全ての成果物はモデルから作る
  • 何かの変更に合わせて徹底的にモデルを更新し続ける
  • 仕様の全てを一枚で表している訳ではないが、仕様の一番信頼できるサマリである

曰く「DDD だと業務の文章でコーディングする」

第一印象

  • 業務の〜もなにも文章は全部日本語じゃん?
  • そもそも、じゃあ DDD でなければ何の文章で実装していると仰るの?

今の考え

  • モデルは単語帳なので、コーディングはできない
  • なのでモデルからユースケース記述を作る
  • ユースケース記述は限りなく実装と表現することが近いテキストである
  • ユースケース記述は単語の扱いが適当だと全く書けない
  • これを真剣に書くほどモデルをどんどん更新する羽目になる[2]

曰く「DDD だと変更に強い」

第一印象

  • DDD だからなの?
  • 例えばテストコードの有無は?
  • そもそも変更って何?
  • あと「強い」ってなに?

今の考え

  • DDD だからではない
  • 設計やテストやドメイン以外のレイヤー、開発プロセスすらも関係する
  • Clean Architecture をまず学ぶ
  • 変更についていろいろ想定して分類するべき[3]
  • 影響を制御して局所化できれば「強い」
  • 普遍技実を主に業務変更に備えるために使っている、だからドメイン駆動という

曰く「DDD は Value Object とレイヤー設計により堅牢」

第一印象

  • DDD 独自のことなの?
  • これをやれば DDD なの?
  • 例えばプリミティブな型を使わなければ堅牢なの?
  • そもそも堅牢って何?

今の考え

  • DDD かどうかは関係ないし、個人的にはどうでも良い
  • 型が意味と制約を持ち、誤用を招きづらい限定的な使い方しかできない状態を堅牢と言う
    • パカパカと getter で数値を開示するただのプリミティブラッパは全然堅牢ではなく脆弱
  • 業務の変更をドメインに、それ以外の変更をドメイン外に影響させるためにレイヤー設計する
    • ライブラリの EoL がドメインに影響しなければ、それはドメインが堅牢というのかも
    • ただしドメイン外をなおざりにして良い訳ではない
  • いずれもドメインを中心とするために採用された普遍技術の一つ
    • 現に僕は自分用のスクリプトでも、要所で VO を作るし簡易 Domain Layer もよく作る

「DDD をやっている」との付き合い方

「DDD をやっている」って砕いて言うと「ちゃんと要件定義 / 要求分析してます」+「保守しやすい設計して良いコード書いてます」+「変更が起きても滞りなく修正して事業継続できます」ってだけの話で、それってエンジニアとして当然のただの研鑽義務なんじゃあないかな

「DDD できない」「どうすれば DDD なのか」なんてことに囚われずに、ひとつずつ自分で砕いてつなぎ合わせていく以外にないんじゃあないかと思う

「DDD をやっているから xx だ」ではなく「業務を中心に据えた分析のアプローチと Clean Architecture の手法が統合された形のひとつを DDD と言うんだ」くらいに捉える方がいろいろ気が楽かなー、と最近は思う

以上


tweet 1

第一印象でやたら「そもそも」って突っ掛かってるな

けど疑ったから理解できたかなと思う

次は喧嘩腰にならずにこのスタンスでやれたら良いなと、自戒の念も込めて呟き


tweet2

最近 ちょっと意識[4] して 本を書いた んだけど、そのおかげかこのポエムは 60 分強でさっと書けた

訓練だなぁ

脚注
  1. サッカー全くわからない ↩︎

  2. 良い意味で強制されると言っている ↩︎

  3. このべき論は自分に当てた自戒 ↩︎

  4. この記事は軽く書いているので厳密に全部は守ってないけど ↩︎

Discussion