マネジメントの知識がドメイン駆動設計を加速する

2023年6月13日開催の設計コミュニティイベント「現場から学ぶモデル駆動の設計 第24回」で発表した資料の説明です。

スライドを公開しています。

概要

今年の三月に出版された佐藤大典さんの『エンジニアのためのマネジメント入門』に書かれている知識は、ドメイン駆動設計を実践する時にとても役に立ちます。

(2023-6-15 「深い洞察に向かうリファクタリング」と「戦略的設計」の内容を加筆)

この本の内容をチームで習得することでドメイン駆動設計をより効果的に進められます。

エンジニアのためのマネジメント入門

『エンジニアのためのマネジメント入門』

https://files.speakerdeck.com/presentations/3f18cb5079be47809bbfa8b99242d649/slide_2.jpg

ドメイン駆動設計』に取り組む時に、特に関係するのはこの4つの章の内容です。

ドメイン駆動設計』

https://files.speakerdeck.com/presentations/3f18cb5079be47809bbfa8b99242d649/slide_3.jpg

エヴァンス氏の『ドメイン駆動設計』は全体は4部で構成されています。

それぞれの部に書かれた内容と『エンジニアのためのマネジメント入門』の内容がどう関係するかを紹介します。

第1部「ドメインモデルを機能させる」とマネジメントの知識

https://files.speakerdeck.com/presentations/3f18cb5079be47809bbfa8b99242d649/slide_4.jpg

ドメイン駆動設計』の第1部は、ドメイン駆動設計の目標を説明しています。

スライドのように『エンジニアのためのマネジメント入門』の第2章と第3章の知識は、このドメイン駆動設計の目標と強く関係しています。

「コミュニケーションを支える技術」と「対話のフレームワーク」は、エンジニアが業務知識を習得し設計に反映させていくための基礎となります。

ドメイン駆動設計が重視する「継続的な学習」とイテレイティブな設計の改善活動は「チームのフェーズ」の変化と、その変化(成長)を促進する「6つのリーダーシップのスタイル」の知識が役に立ちます。

第2部「モデル駆動設計の構成要素」とマネジメントの知識

https://files.speakerdeck.com/presentations/3f18cb5079be47809bbfa8b99242d649/slide_5.jpg

ドメイン駆動設計の「エンティティ」「値オブジェクト」「モジュール(パッケージ)」「集約」は、業務知識を習得し表現するためのパターンです。

ドメイン駆動設計に書かれているパターンを実践するために必要なのは、業務の理解です。

『エンジニアのためのマネジメント入門』で紹介されている、バリューチェーン、オーガニグラフ、営業プロセスは事業活動を理解するための全体地図として使えます。管理会計や収益構造の知識は、業務ルールのwhyを理解し、whatを記述するための基礎知識になります。

そういう業務知識(ドメインの知識)をクラス名やパッケージ名として整理して関連づけていくのがドメイン駆動設計の基本的な活動です。

第3部「深い洞察に向かうリファクタリング」とマネジメントの知識

https://files.speakerdeck.com/presentations/3f18cb5079be47809bbfa8b99242d649/slide_6.jpg

ドメイン駆動設計』がほんとうに効果を生み出すのは「深い洞察に向かうリファクタリング」に取り組んだ時です。

そのためには、さまざまな関係者と密度の高いコミュニケーションを繰り返しながら暗黙の概念を掘り起こし、それをプログラミング言語を使って明示的に表現することが必要です。

業務知識の発見と表現の改善を繰り返すためのリファクタリングドメイン駆動設計の核心の活動です。

この核心の活動を支えるのが「ファシリテーション」の基礎知識であり、「リーダーシップ」であり、「ステークホルダー」の特定と建設的なかかわり方です。自分がわかっている/わかっていない、他人がわかっている/わかっていないの「ジョハリの窓」の理解も、深いモデルに近づくために役にたつでしょう。

また深いモデルにたどり着くためには、オーガニグラフ、ステークホルダーマップ、バリューチェーンなど俯瞰的なものの見方が役に立ちます。特定箇所の深堀をしつつ、それが全体の中でどういう位置づけで、他の要素とどう関係するかを理解することが、ドメイン駆動設計でもマネジメントでもよい成果を生み出します。

SECIモデルの内面化・共同化・表出化・連結化のサイクルは、「深い洞察に向かうリファクタリング」の活動サイクルそのものです。

第4部「戦略的設計」とマネジメントの知識

https://files.speakerdeck.com/presentations/3f18cb5079be47809bbfa8b99242d649/slide_7.jpg

ドメイン駆動設計』の戦略的な設計に取り組む時に土台となる知識が『エンジニアのためのマネジメント入門』第4章「組織のマネジメント」と第5章「戦略実現のためのマネジメント」の内容です。

キャリアとしてエンジニアリングマネージャーを目指すかどうかにかかわらず「組織」や「事業戦略」の知識を広げることは『ドメイン駆動設計』を取り入れたソフトウェア開発の基礎であり、また設計活動の軸になります。

異なる境界づけられたコンテキストをどうつなぐかのパターンは、技術的な選択肢というよりは、チーム間や組織間の関係性とコミュニケーションの問題です。「顧客と供給者」の関係を気づくのか、「順応者」や「腐敗防止層」で対応するのか、「別々の道」を選ぶかは、自分たちの組織のバリューチェーンやオーガニグラフの関係をどう捉え、その構造をどうソフトウェアの構造に反映させるか、という設計課題です。

「大規模な構造」の検討は、バリューチェーンやオーガニグラフによるモデル化と直接対応します。

コアドメインを明確にする「蒸留」に必要なのは自社の事業戦略です。事業を存続し発展させていくための競争優位を生み出す中核の活動をどこに求めるかを正しく理解し、それをソフトウェアの設計に活かしていくことで、事業価値を生み出すソフトウエアを開発し発展させていくことができます。

事業戦略が抽象的だったりうまく言語化されていない状況は少なくありません。そのような場合でも、ソフトウェアのどこが複雑になり、どこが重要な変化箇所になることが多いか、というソフトウェアの構造や変更の履歴から、事業戦略を理解する重要な手がかりを得ることができます。そうやってソフトウェアの側から分析していく場合でも、マネジメントの視点からの事業戦略の考え方や枠組みについての知識があれば、より効果的に分析を進めることができます。

『ドメイン駆動設計』の5つの基本アイデア

エヴァンス氏の『ドメイン駆動設計』の背景にある基本アイデアは何かという私の捉え方のメモ書き。

ドメイン駆動設計にはいろいろな側面がある。また書籍『ドメイン駆動設計』は体系だった設計方法論ではなく、設計の考え方とやり方を経験則として言語化してみた、と捉えている。

その経験則(100%ではないが多くの場合に役に立つ原則)の背景にあるエヴァンス氏の基本的な発想は次の5つに要約できると考えている。

ソフトウェアの複雑さは事業活動の複雑さに起因する

技術的な複雑さもあるが、ソフトウェアが複雑になるのは対象領域の複雑さが主たる理由という考え方。

業務アプリケーションであれば、事業活動の複雑さが業務アプリケーションの複雑さの原因と捉える。

ドメイン駆動設計は、この事業活動の複雑さに起因するソフトウェアの複雑さをうまく扱うための工夫、というのが私の捉え方。

ドメイン駆動設計という設計のアプローチを取り入れるなら、まず第一に対象領域の複雑さに焦点を合わせることが大前提になる。

モデル駆動設計の効果

複雑さに取り組む時の工夫の大きな柱がこれ。 モデルと実装を一致させることで複雑さが扱いやすくなる、という考え方。

エヴァンス氏が「まえがき」で紹介している三つのプロジェクトの例で「精緻なモデルを作ったが実装と乖離(かいり)してうまくいかなった」という経験からモデルと実装を強く結びつけ、モデルとコードを相互に作用させながら設計を改善していくという発想が生まれたのだろう。

ドメインモデルの活用

モデルといっても、プロセスモデルユースケースモデル、データモデル、状態遷移モデルなどいろいろなモデルがある。

ドメイン駆動設計が「モデル駆動設計」で重視しているのは「ドメインモデル」。エヴァンス氏のドメインモデルの解釈はファウラー氏と同じ「オブジェクト指向」のモデルであることが基本になっている。

ユースケースモデルやデータモデルとの一番の違いは、複雑な業務ロジックをどう記述するかを重視する点。

業務ルールに基づく複雑な計算判断ロジックをうまく整理する方法として、トランザクションスクリプトやテーブルモジュールではなくドメインモデルを選択するという考え方。

また、ドメインモデル作りは、開発者が業務知識を取得する手段の一つであり、関係者が同じ言葉(ユビキタス言語)を使って意図を伝えあう語彙の骨格になり、モデルとコードを一致させる道具である、という発想。

深い洞察に向かうリファクタリングの重要性

設計改善の手法としてかなり普及してきたリファクタリングの考え方を、事業活動を深く理解するための手段として活用する、という発想。

リファクタリングの目的として以下の二つを重視する。

  • 暗黙の概念の発見と言語化
  • ソフトウェアの発展性の改善

複雑な事業活動で価値を生み出すソフトウェアの開発は、最初からうまくいくわけではない。表面的かつ断片的な知識を元にした設計から出発して、実際に動くプログラムを記述し、そのコードをリファクタリングをしながら、事業活動の深い理解と、ソフトウェアの構造の改善に継続的に取り組みつづけるという発想。

戦略的な設計への取り組み方

規模が大きなソフトウェアを長期的に成長させていくという課題に対する『ドメイン駆動設計』の考え方とやり方。

  • コードに責任を持つ開発者が全体的・長期的な設計課題に取り組むことの重要性と効果
  • そのために開発の文脈を明確に区切って、全体的・長期的な課題を明確にして扱いやすくする
  • 文脈と文脈の境界で生まれる課題とその対応方法の選択肢の検討
  • クラス粒度ではあつかえない複雑さの扱い方の工夫(レイヤー構造や蒸留)

『ドメイン駆動設計』の解説記事を書きました

本日(1月18日)発売された、Software Design誌 2023年2月号の第一特集で「ドメイン駆動設計入門」を書きました。 執筆の意図と記事の概要を簡単にまとめておきます。

Software Design 2023年2月号|技術評論社

執筆の意図

特集のサブタイトルにある通り「設計力を磨きたい」読者が、ドメイン駆動設計の基礎を知ることで「設計の手法とアイデアの引き出し」を増やすことの役に立てればと思い執筆を引き受けました。

重視したこと
  1. 断片的な用語やパターンの解説でなく、ドメイン駆動設計の全体像と要点を伝える
  2. 全体像を伝えるための図や表を多めにした(ソースコードの例は少ない)
  3. 全体像と要点は、原典である『エリック・エヴァンスのドメイン駆動設計』(以下『ドメイン駆動設計』)の説明を中心にした
  4. ドメイン駆動設計の具体例として『ドメイン駆動設計』に出てくる国際海上貨物輸送の具体的な業務知識の解説を多めにした
  5. マイクロサービスなど分散アーキテクチャドメイン駆動設計の関係など、新しめの内容を盛り込んだ

基本的にはエヴァンスさんの『ドメイン駆動設計』の概要の説明です。

まだエヴァンス本を読んだことがない人や、読んだことはあるけどなんかよくわからんかったという人が、この記事を読むことで、全体の流れと要点をつかみ、エヴァンス本を興味深く読めるようになるとよいな、という思いで記事を書きました。

ドメイン駆動設計とは?(本特集記事の第1章)

ドメイン駆動設計の基本的な考え方を説明する情報が少ないと感じていました。 そういう問題意識から、この特集の第1章では『ドメイン駆動設計』の「はじめに」と第1部「ドメインモデルを機能させる」の3つの章をわかりやすく説明しようと考えました。

まず、ドメイン駆動設計の基本的な考え方を以下の図の流れで説明しています。

次に、ドメイン駆動設計という設計のやり方の中心となるドメインモデルとその三つの用途について書いています。(ドメインモデルの作り方は本特集記事の第2章で具体的に説明しています)

また、ドメインモデルを中心にどのようにアプリケーション全体を組み立てるかを、ヘキサゴナル(ポート &アダプタ)アーキテクチャやクリーンアーキテクチャの概要に触れながら説明しています。

ドメインモデルを理解しよう(本特集記事の第2章)

ドメイン駆動設計の中心となるなるドメインモデルの作り方についての解説です。

題材は『ドメイン駆動設計』のあちこちにでてくる国際海上貨物輸送のオーバーブッキングルールです。

図のような単純な理解からはじまり、業務知識を広げながらドメインモデルを成長させていくやり方を説明しています。

オーバーブッキングルールが、国際海上貨物輸送ビジネスにとっていかに重要であるか、そして、どういう複雑さがあるかという業務知識に触れながら、値オブジェクトなどを使ってドメインモデルを組み立てる道筋を紹介してみました。

国際海上貨物輸送の業務そのものについての説明が多めです。そこに興味が持てれば、ドメインモデルを組み立てるパターンの説明も理解しやすいと思います。

業務を理解するために次の三つの視点を組み合わせるやり方を説明しています。ドメインモデルの中心となるのは業務ルールの視点です。他の二つの視点と関連づけることで、業務の理解が立体的になります。

  • 業務プロセス
  • 業務データ
  • 業務ルール

また、業務ロジックを表現するための三つのパターンを簡単に比較しながら、ドメインモデルの特徴を説明しています。

分散アーキテクチャドメイン駆動設計(本特集記事の第3章)

最近は、分散アーキテクチャやマイクロサービスとの関係でドメイン駆動設計が取り上げられることが増えてきました。 この章では、ドメイン駆動設計の考え方とやり方をどのように分散アーキテクチャに活かしていくかを説明しています。

ドメイン駆動設計』が書かれた当時は、分散アーキテクチャはそれほど一般的ではありませんでしたが、この本の第4部で紹介されている大規模なシステムに取り組むための「境界づけられたコンテキスト」と「コンテキストマップ」の考え方が、最近では分散アーキテクチャのモデルとして利用されることが多くなってきています。

「境界づけられたコンテキスト」はどう分けたらうまくいくか、「コンテキストマップ」はどうつなげばよいかを設計するための考え方とやり方です。

また、さまざまな業務領域をそれぞれの特性ごとに適切な設計や開発のやリ方を検討するために『ドメイン駆動設計』の「コアドメイン」という考え方が役に立ちます。

ドメイン駆動設計のパターン名と用語集(本特集記事の第5章)

ドメイン駆動設計のパターン名や用語は、意味がわかりにくく、人によって捉え方にばらつきが多いようです。 この章では、主要な用語を取り上げながら、その用語の一般的な意味と『ドメイン駆動設計』という文脈に限定した場合の意味について説明しています。

また、用語の意味は他の用語との関係で理解することで、理解しやすくなります。図のように、主なパターン名と用語を関連づけながら、主要な用語を解説してみました。

設計の手法とアイデアの引き出しを増やす

ドメイン駆動設計は銀の弾丸でもないし、唯一の正しい設計技法でもありません。 しかし、ドメイン駆動設計の考え方とやり方を理解することで、設計の手法やアイデアの引き出しは確実に増えるはずです。

ここで紹介したSoftware Design 2023年2月号の特集記事が設計力を磨きたいと思っているソフトウェア開発者のお役にたてればうれしいかぎりです。

本の紹介『読みやすいコードのガイドライン』

はじめに

良い本です。コードを書く人であればだれでもが気づきと学びがあるでしょう。
特に、コードの規模が大きく長期間にわたってさまざまな関係者が読むことになるプログラムを書く人にはぜひ読んでほしい本です。

gihyo.jp

この本から学べるのは、どちらかというと「やり方」よりも「考え方」です。
書き方の具体例というよりは、良いコードを書くための考え方を、著者の知見に基づいて丁寧かつ具体的に説明しています。 別の言い方をすると、とにかくコードの具体例で「やり方」を覚えたい、という人には合わないかもしれません。
コードの具体例もたくさん登場しますが、それは「考え方」を説明するための例であって、コードの書き方のお手本やサンプルを集めた本ではありません。

サンプルコードはKotlinです。内容は、どちらかといえばユーザーインタフェースよりという印象です。しかし、Kotlinを知らなくてもほぼ問題なく読めますし、「考え方」の本なのでサーバーサイドのプログラミングでも役に立つ内容ばかりです。

この本の内容は次の三つに分けることができます。

  • 基本となる考え方の説明(はじめに、1章)
  • 自然言語を使った人と人とのコミュニケーション(2章、3章、7章)
  • プログラムの構造と複雑さの改善(4章、5章、6章)

それぞれに内容について、概略を紹介します。

基本となる考え方(はじめに、1章)

コードの読みやすさが開発の生産性に与える影響を述べています。
その視点から、コードの読みやすさの指標を4つあげています。

  • 単純なコード
  • 意図が明確なコード
  • 独立性の高いコード
  • 構造化されたコード

この本で説明している「読みやすいコード」の考え方を要約した内容です。

また、この考え方にそって、さまざまな一般的な設計原則のなから、以下を取り上げて説明しています。

この五つを代表的な原則として選んだ理由として「過剰に適用したとしても比較的悪影響が出にくい」ことをあげているところに、著者の設計原則についての考え方が表れていると感じました。

自然言語を使った人と人とのコミュニケーション(2章、3章、7章)

コードを書くとは、人と人とのコミュニケーションである、という観点で参考になるのが次の三つの章の考え方でしょう。

  • 第2章 命名
  • 第3章 コメント
  • 第7章 コードレビュー

自然言語を使ってうまく意図を伝達するには、どのようなことに気を付ければよいかを説明した内容です。
ソフトウェア開発とはコミュニケーションでありドキュメンテーションである、という考え方が一貫しています。コードの規模が大きく関係者も多い開発の現場で、ほんとうに役に立つ考え方をわかりやすく解説しています。
チーム開発に関わる人にはぜひ読んでほしい内容です。

プログラムの構造と複雑さの改善(4章、5章、6章)

個人的には、この三つの章が特に参考になりました。コードの書き方というよりは、設計がテーマの章だと思います。
コードが複雑になり読みにくくなる原因とその改善策を「状態」「関数」「依存関係」の視点から説明しています。

第4章 状態

状態を単純化する方法がいろいろ紹介されています。
状態の扱いはプログラムを複雑にします。この複雑さの扱い方について、学びの多い章です。
複数の変数を扱うときの「直交性」の大切さ、状態遷移の複雑さを緩和するための「不変性」「べき等性」「巡回と非巡回」などの考え方を具体例や図を使いながら説明してあります。

かなり高度な内容が含まれていると思いますが、それをわかりやすく説明できているのは、著者がソフトウェア開発の現場で、こういう考え方を回りに何度も伝えてきた経験が背景にあるのだろうと感じました。

第5章 関数

関数の動作を予測可能にする、という視点からのコードの書き方を説明しています。
「関数の責任の分割の考え方」と「関数の流れをわかりやすくする工夫とその効果」が説明されています。
「早期リターン」というある意味で単純なテクニックを取り上げて、5ページにわたって背景にある考え方、適用する時の注意点が丁寧に説明されています。こういうところがこの本の特徴であり、この本を読む価値だと思います。

第6章 依存関係

クラスの依存関係というやっかいな課題を「結合度」「方向」「重複」「明示性」の四点から取り上げています。
依存の強さ(結合度)について、7つの結合パターンと結合度の強弱についての説明は、とてもわかりやすく参考になります。考え方自体は従来からあるものですが、従来からの考え方について、いろいろ調べ、それを現場に取り入れてきた著者の知識と経験がにじみ出ている内容だと感じました。
「方向」「重複」「明示性」については、図とサンプルコードが丁寧に作られていて、なかなかの力作だと思いました。この本のもとになったプレゼンテーション、講義、現場での意見交換などからのフィードバックか活かされているのでしょう。

この三つの章は、コードの書き方というよりは設計の考え方とやり方として大いに参考にしてほしい内容です。

関連書籍など

この本に関連した書籍をいくつか取り上げておきます。

O'Reilly Japan - リーダブルコード

コードの書き方についての古典的な名著です。
取り扱っている内容は、本書と重なるところもありますが『リーダブルコード』のほうがコードよりで基礎的な内容だと思います。

『読みやすいコードのガイドライン』はわかりやすく書いてありますが、取り上げている内容や考え方の説明は、初級者向けというよりは中級者向けだと思います。

良いコード/悪いコードで学ぶ設計入門 ―保守しやすい 成長し続けるコードの書き方:書籍案内|技術評論社

仙塲さん(ミノ駆動さん)のこの本も内容としては重なるところがあります。
仙塲さんの本は、現場の生々しいコード例が豊富で、コードの具体例で学びたい人には、こちらのほうが合うかもしれません。しかしこちらも単なるコードのサンプル集ではなく「設計の考え方」に触れることを強く意識している本です。
リファクタリングを通じた設計改善の指針と具体例という点で、こちらの本も合わせて読むことをお勧めします。

最後に『読みやすいコードのガイドライン』とは少し異なる視点で書かれた本を2冊紹介します。
これらの本の背景にある設計の考え方は、かなりの部分が共通しているので、参考になる点も多いと思います。

達人プログラマー(第2版) 熟達に向けたあなたの旅 | Ohmsha
現場で役立つシステム設計の原則 〜変更を楽で安全にするオブジェクト指向の実践技法:書籍案内|技術評論社

(拙著です)

変数(variable)と値(value)

はじめてScalaに触れたとき、変数宣言(var)と値宣言(val)を使い分ける言語仕様に、なるほどなあ、と思った。簡単に言えば、変数(var)は再代入できて、値(val)は再代入できない。

プログラミングのスタイルとして、var宣言は命令的なプログラミング、val宣言は宣言的なプログラミングになる。どちらのプログラミングスタイルで書いているかを、varとvalで明示できるわけだ。

Javaだと言語の基本の仕組みはすべてが変数。final宣言をすることで再代入をコンパイルエラーにすることはできる。Javaは、C言語C++などの命令的なプログラミングの系譜の言語なのですべて変数(variable)というのは、とうぜんの言語仕様だった。

命令的なスタイルから宣言的なスタイルに

命令的なプログラミングでは変数(variable)を使う。宣言的なプログラミングでは値(value)を使う。

再代入可能な変数(variable)は、プログラムの実行時の状態が基本的には不定(不安定)になる。

それに対して再代入できない値(value)は、プログラムの実行時の状態が仕組みとして不変になり安定する。

宣言的なスタイルを選ぶ

プログラミングのアプローチとしては、命令的なスタイルより宣言的なスタイルのほうが良い。プログラムの実行時の挙動が安定することは、良いプログラムを書くために大きなアドバンテージになる。

変数(variable)と値(value)の使い分け

ScalaやKotlinのように変数(var)と値(val)を明示的に使い分けることができる言語だと、命令的なスタイルと、宣言的なスタイルを明示的に書き分けることができる。

Javaのfinal宣言

Javaだと、final宣言を使うことで、いちおう宣言的なスタイルを表明することはできる。ただしfinal宣言した変数が参照するオブジェクトの状態が変更可能であると、簡単に命令的なプログラミングに逆戻りしてしまう。

実際にやってみると

私自身は可能な限り宣言的なスタイルでプログラミングをしたいので、ScalaやKotlinでいろいろ書いてみた時は、変数宣言(var)はまったく使わず、値宣言(val)だけで書いた。

valだらけの気持ち悪さ

そうして書いたコードを眺めてみると、valだらけで、valという宣言がほとんど意味のないノイズに近い印象をうけた。その時思ったのは、何も宣言しなければ、デフォルトで値宣言(val)になり、どうしても変数宣言(var)にしたいところだけvarを記述する、という言語であればもっとよいのになあ、と思った。

final だらけの気持ち悪さ

Javaの場合は、もっとひどい。言語仕様としては、インスタンス変数・メソッドの引数・メソッドのローカル変数・クラス変数をすべてfinal宣言にできる。

しかし、宣言的なスタイルの表明ということで徹底的にfinal宣言を書きまくると、ScalaやKotlinのval宣言だらけよりも、もっと読みにくいひどいコードになる。わかりきった場所までfinalを書くことに「いったいなんの意味があるのか」と疑問に思うくらい、final宣言が邪魔に見えてくる。

Javaの場合、final宣言を徹底しないとfinalで宣言した参照先のオブジェクトで変数を上書きされてしまう可能性が高い。標準ライブラリで用意されているList・Set・Mapなどは、変更可能なオブジェクトなので、final Map map; というような宣言をしても、実質的には、宣言的なプログラミングではなく命令的なプログラミングのままになる。つまり実行時のmapの状態は不定(不安定)なままなのだ。

しかし、そこまでfinal宣言を書くことは単純に面倒だし、書けば書くほどコードが読みにくくなるのが現実。

じゃあどうするか

ScalaやKotlinだとvalだらけになるのは、言語仕様上しかたがない。ところが、Javaの場合は、別の選択肢がある。

finalを書かないという選択肢

それはfinalを書かないという選択肢である。

もちろん、言語仕様的にはfinal宣言をしていない以上、それは再代入可能な変数であり、見た目は命令的なプログラミングそのものである。

ただしプログラミングの方針あるいは約束事として、宣言的に書く、変数の再代入はしない、ということを大前提として共有できていれば、ノイジーで面倒くさいfinal宣言をまったく書かないことも選択できる。

ScalaやKotlinのように値(val)を取り入れるた言語では、宣言的に書くにはvalを書かざるを得ない。 しかし、値(val)の仕組みがないJavaの場合、それを逆手にとって、約束として再代入不可を共有してfinalは書かないというアプローチを選択できるわけだ。

ここらへんは、ふつうのぷろぐらまのirofさんのこの記事も参考になる。

finalを付けるのをやめてみた(日々常々)

final書かずに、宣言的なプログラミングスタイルで意思統一しよう

私は、finalを書いても命令的なプログラミングのままのコードをたくさん見てきた。

final宣言の記述を強制しても、コードを書く人間の頭は命令的なプログラミングスタイルのままだということ。

そこの発想を切り替えるには、final宣言の記述のルール化よりも、変数に再代入をしない、そうすることでプログラムの挙動が安定するし、プログラムのロジックがシンプルになる、という宣言的なプログラミングスタイルの考え方とやり方を粘り強く伝え、それを共有知にすることに時間とエネルギーを使う方が費用対効果が高いと思って、日々の開発に取り組んでいる。

実際のところ、表面的にfinal宣言だらけで書いているチームよりも、final宣言は書かないが、変数には再代入をしないプログラミングスタイルを共有できているチームのほうが、読みやすく質の高いコードを生み出せている手ごたえがある。

f:id:masuda220:20211128085518j:plain

ドメイン駆動設計というソフトウェア開発のやり方

この記事はドメイン駆動設計を取り入れたソフトウェア開発を実際にどのようなやり方で進めているかの事例紹介です。

2021年10月のモデルベースで要件定義をやってみた - connpassで発表した資料の解説です。

設計の効果

良い設計は悪い設計よりも変更が楽で安全です。

ドメイン駆動設計は、そういう良い設計を目指すための設計の考え方とやり方の一つです。

ドメイン駆動設計の特徴は次の2つです。

  • ソフトウェアで扱うさまざまなロジックとデータをドメインに焦点を合わせて整理して記述する
  • 関連するロジックとデータを一つのクラスにカプセル化する

ドメインとはソフトウェアの対象領域です。業務アプリケーションであれば、事業活動ドメインです。この記事では、ドメイン(事業活動)に焦点を合わせたクラス設計の考え方とやり方の基本的な流れを紹介します。

画面やユースケース中心のソフトウェア開発

アプリケーション開発でありがちなのは、画面やユースケースを要件として定義して、画面の入力をデータベースに記録しデータベースから取り出したデータを画面に表示するためのコードを書く、というやり方です。

ありがちなソフトウェア開発
ありがちなソフトウェア開発
動くソフトウェアを作るためには当たり前のやり方です。しかし、このやり方では画面や機能が増えソフトウェアの規模が大きく複雑になると、ソフトウェアの変更がやっかいで危険になります。 機能一覧やバックログリストを作って、ひとつひとつの機能をばらばらに作っていくと、あちこちに似たようなコードを重複して記述することになります。また、データベースにデータを書き込む機能と、そのデータを読み取って利用する機能との間の関係がどんどん見えにくくなります。

その結果、どこに何が書いてあるかのわかりにくく、ちょっとした変更が思わぬ副作用を引き起こす変更がやっかいで危険なプログラムになってしまいます。

モデルベースの要件定義

このような問題を解決するために考案された要件定義の手法がRDRA(ラドラ)です。 バリューソースの神崎さんが提唱されたモデルベースで軽量で柔軟な要件定義手法です。

ソフトウェア開発の視点を増やす

RDRA 2.0 視点を増やす
RDRA 2.0 視点を増やす
要件を定義するために、画面やユースケースだけではなく、ビジネスをとらえる視点を増やします。

  • ビジネスユースケースと業務フロー
  • ビジネスルール
  • 業務上で意識している状態の遷移
  • 業務で関心のある情報

画面やユースケースを個別に定義するだけでは見落としがちな、さまざん情報を取り入れることで、いままではっきりしなかった画面やユースケース目的や、画面間・ユースケース間の関係が見えるようになります。

関係づけて考える
RDRA 2.0 関係で考える
関係で考える

RDRA(Relationship Driven Requirement Analysis)の特徴は、関係(relationship)で考えることです。

さまざまな視点の情報を関係づけることで、情報の意味が明確になります。また関連づけて考えることで、それぞれの視点だけでは見落としがちな情報を発見できます。

要件モデルの品質をあげる

複数の視点を関連づけることで、それぞれの視点のゆがみや、視点間の不整合が見つかり、ゆがみや不整合を修正することで、要件モデルの品質が向上します。

事業活動と関係づける

さまざまな視点のモデルを総合的に関連づけるために役に立つのが事業活動の俯瞰モデルです(図の左上)。 事業活動は、さまざまな業務活動をばらばらに進めているわけではなく、お互いに関連しながら活動しています。表面的にはわかりにくいかもしれませんが、企業は収益を上げ続けるための組織的な行動をするための仕組みです。すべての活動はなんらかの形でつながっています。

事業活動まで視野を広げることで、要件定義のさまざな視点の情報を適切に関連づけ、全体を一貫したモデルとして体系だてることができます。

ドメイン駆動設計

事業活動にまで視野を広げることはドメイン駆動設計というソフトウェア開発のやり方の必要条件です。

しかし、事業活動まで意識してソフトウェア開発をすることのたいせつさは、ドメイン駆動設計に限らず、さまざなソフトウエア開発の考え方で強調されています。

ではドメイン駆動設計ならではの特徴とはなんでしょうか?

エヴァンスの『ドメイン駆動設計』に書かれている内容の中でも、ドメイン駆動設計ならではの特徴がはっきりと表れているのは次の3つの章です。

  • 第3章 モデル駆動設計(モデルと実装を結びつける)
  • 第10章 しなやかな設計(ソフトウェアの変更を楽で安全にする)
  • 第15章 蒸留(コアドメインに集中する)

この三つの章は『ドメイン駆動設計』の中では、読みにくい箇所かもしれません。エヴァンスが何を伝えたいのか私も最初はチンプンカンプンでした。しかしアプリケーション開発の現場で、変更を楽で安全にするための良い設計を模索し、この本を何度も読み返す中で、この三つの章がドメイン駆動設計の考え方とやり方の要点であることがだんだんとわかってきました。 この三つの章が理解できると、他の章を読む時に、この三つの章の準備や応用として位置付けて解釈ができるようになります。

事業活動に焦点を合わせる

ドメイン駆動設計のアプローチ:事業活動に焦点を合わせる
事業活動に焦点を合わせる
ドメイン駆動設計というソフトウェア開発のやり方の基本は、事業活動のモデルをいつも意識することです。

技術者にとっては最初は事業活動のモデルといってもぼんやりとした、おそらくはまちがったイメージしか持つことができません。しかし、ソフトウェアの開発を通じて、一貫して事業活動を意識しつづけることで、しだいに事業活動のモデルがしっかりしたイメージになってきます。

事業活動のモデルをしっかりと認識できるようになった開発チームの生み出すソフトウェアは、事業活動に適切な価値を提供することができるようになります。

事業活動の仕組みと決め事の言語化(可視化)

事業活動の仕組みと決め事の言語化と可視化
事業活動の仕組みと決め事の言語化と可視化
事業活動の知識が増えるだけでも、ソフトウェア開発には良い効果が表れます。つまらない勘違いや見落としが減ることで、よりニーズにあったソフトウェアを目指した開発ができるようになります。

しかし、事業活動の知識が増えただけでは、良い設計はできません。良い設計をするために重要なのは、事業活動の仕組みと決め事(ビジネスルール)を言語化し可視化する活動です。

アプリケーションが複雑になる大きな要因は、事業活動の仕組みと決め事が複雑だからです。事業活動の仕組みと決め事を整理して扱いやすくすることは、アプリケーションの複雑さを整理し扱いやすくすることに直結します。

ビジネスルールをソフトウェアで表現する

ドメイン駆動設計 ビジネスルールをソフトウェアで表現する
ビジネスルールをソフトウェアで表現する
ドメイン駆動設計を取り入れることで良い設計を生み出す活動の核心が、整理したビジネスルールをソフトウェアで表現するために、オブジェクト指向プログラミングの技法を活用することです。

オブジェクト指向プログラミングの技法といっても難しい話ではありません。

  • 業務的に関連するロジックとデータを一つのクラスのまとめる(カプセル化
  • クラス名やパッケージ名に業務の用語をそのまま使う
  • クラス間の関係、パッケージ間の関係を業務の構造と一致させる

の三つを地道に丁寧に実践するだけです。

この活動は難しいわけではありませんが、時間がかかります。ロジックとデータのうまいまとめ方や良いクラス名や良いパッケージ名が最初から見つかるわけではありません。

継続的なリファクタリング(設計改善)

ある時点の理解をもとに試しに作ってみて、作ってみた内容を関係者で評価しながら、ぎごちなさや分かりにくい箇所をよりよい設計に改善を試みる。こういうリファクタリングの活動が延々と続くのがドメイン駆動設計というソフトウェア開発のやり方です。

そして良い設計を目指したリファクタリング(設計改善)を地道に丁寧に続けるから、変更が楽で安全なソフトウェアに育てていくことができるのです。

収益構造と事業方針に焦点をあわせる

ドメイン駆動設計 収益構造と事業方針に焦点をあわせる
収益構造と事業方針に焦点をあわせる
事業活動を表現するありとあらゆるクラスに膨大なリファクタリングを実践することは現実的ではありません。時間と費用がかかるわりには大きな効果を生み出すことはできません。

中核の課題に焦点を合わせる

ドメイン駆動設計の費用対効果をあげるために、設計改善の対象を絞り込み、その対象とする範囲の設計改善に集中することが重要です。

もちろん、どこに集中すればよいかを特定すること自体がたいへんな活動になります。しかし、どこでもいいからせっせとリファクタリング、というアプローチと、どこに集中すべきかを模索しつづける、というアプローチとでは、生み出す成果に大きな違いが生まれます。

収益構造と事業方針を理解して設計する

どこに集中すべきかの模索の重要な手がかりが、その事業の収益構造と事業方針です。

収益構造は売上 ー 費用です。売上をどうやって生み出していて、どこに費用が生まれているか、という仕組みを理解することで、どこに集中すべきかを判断しやすくなります。 そして、事業活動には必ず独自の事業方針があります。ビジョンやミッションという抽象度の高い方針の理解も有用ですが、ソフトウェア開発に直接的に役に立つのは、もっと具体的な決め事として現れる事業方針です。

具体的な決め事に焦点を合わせる

たとえば、価格設定のルールや割引適用のルールです。 あるいは、いつ何をどのくらい発注するかの決め事や、キャンセル・返品・返金に関する決め事です。 これらの決め事は基本は単純ですが、実際の業務では、顧客のカテゴリー、商品の種類、取引先との関係、季節や時間による条件分けなど、さまざまな要素が複雑に絡み合ってきます。

これらの決め事は、安定した収益を確保し、事業を存続させ発展させるための決め事です。こういう決め事に焦点を合わせることで、設計を重点的に改善すべきクラスやパッケージを特定する手がかりが得られます。

全体への波及

重点対象としたクラスやパッケージの設計改善を続けると、結果的にはそれ以外のクラスやパッケージの設計も改善されます。さまざまな事業活動やそのための仕組みと決め事はなんらかの関係でつながっています。収益構造や事業方針に直結するクラスやパッケージを集中的に改善することで、そのクラスやパッケージに隣接したクラスやパッケージの改善が進みます。そういう隣接したクラスやパッケージの改善がさざ波のようにアプリケーション全体につながっていくのです。

事業方針とコードを直接的に関係づける

収益構造や事業方針はソフトウェアの設計や実装から遠く離れた関心事ではありません。日常の設計活動に直接的に関係します。

たとえば、このクラスとパッケージを分割すべきかいっしょにすべきか、という開発現場の日常的な迷いの答えも、事業方針を考えれば、明確な指針が見つかります。 汎用的なAPIにすべきか、ビジネスルールの違いを反映した個別のAPIに分割すべきかの答えは、事業活動の仕組みと決め事、今後、どのように事業を発展させていきたいかの方針から判断すべき設計課題なのです。

こういう設計を日々くりかえすことが、私が実践しているドメイン駆動設計というソフトウェア開発のやり方です。

ドメイン駆動設計の主活動と補完活動

f:id:masuda220:20211102071225j:plain
ドメイン駆動設計というソフトウェア開発のやり方
ドメイン駆動設計では、事業活動の仕組みや決め事に焦点をあわせて、ビジネスルールに基づく計算判断ロジックをクラスとして表現することが設計活動の中心です。そういう事業活動に焦点を合わせて設計したクラスの集まりをドメインモデルと呼びます。 しかし、アプリケーションとして完成するには、ドメインモデルだけではなく、事実を記録し参照するための画面やデータベース操作を扱うためのクラス設計も必要です。ドメイン駆動設計では、こちらのクラス設計は補完的な活動になります。

主軸の設計活動と補完する設計活動は相互に関連します。

主軸となるドメインモデルのクラス設計をするときには、画面やユースケースの視点から設計の妥当性を検証します。画面やユースケースを実現するためのクラス設計は、ドメインモデルのクラス設計の視点から検証します。

こういう双方向の設計のフィードバックがより良い設計を生み出します。

ドメイン駆動設計の集約のわかりにくさの原因と集約を理解するためのヒント

ドメイン駆動設計』のモデル要素のひとつとして「集約」があります。 アプリケーションの対象となる事業活動の仕組みや決め事をソフトウェアで表現する技法のひとつとして集約の考え方はとても役に立ちます。

集約パターンはデータベースのデータ整合性の視点での説明されることが多いようです。しかしデータ整合性の文脈で集約を理解しても、ドメイン駆動設計の中核の関心事である「ドメインの複雑さ」を理解しドメインの知識をクラスで表現するためにはあまり役に立ちません。

この記事では、集約パターンをドメインロジックを表現するモデルの構成要素として効果的に利用するためのヒントを提供したいと思います。

集約はデータ操作の道具ではありません。集約はビジネスルールにもとづくドメインロジックのモデリングと実装の手段です。ここがわかるとドメイン駆動設計の理解が一気に進むと思います。

どうして集約がデータ整合性の話になってしまうのか

集約は「第6章 ドメインオブジェクトのライフサイクル」で基本パターンのひとつとして説明されています。 この章はオブジェクトの永続化と復元をとりあつかっています。集約パターンのほかにリポジトリパターンとファクトリーパターンが登場します。

6章のオブジェクトのライフサイクルの関するパターン、特にリポジトリの考え方はそれ自体はドメイン(問題領域)に由来しません。エヴァンスが書いているとおりです。

6章はオブジェクトの永続化と復元の複雑さを「ドメインモデルの本来の関心事」から分離するためのパターンの紹介です。この章は他の章と異なり、ドメインの知識をどうモデリングしクラスとして表現するかよりも、データベース操作と密接に関係した内容を中心に書かれています。『ドメイン駆動設計』の中で、この章だけがドメインロジックよりもデータベースに関心事の焦点が当たっています。

その結果、この章で登場する集約パターンはデータベース操作やデータ整合性の視点からの説明が多く、またそう理解している人も多いようです。

データの記録と参照はアプリケーションの重要な関心事

アプリケーションの機能として、データの記録と参照は重要な関心事です。設計や実装に多くの時間を使うのは、データの記録と参照かもしれません。その観点からすれば、集約を「データの集約」として理解し、データベースのトランザクションと関係づけて考えるのが自然だし、多くの開発者にとって理解がしやすいのかもしれません。

ドメイン駆動設計の関心の焦点はデータではなくロジック

しかし、ドメイン駆動設計の関心の焦点はデータの記録と参照ではありません。ドメイン駆動設計が立ち向かおうとしているソフトウェアの複雑さはデータの扱い方の複雑さではなく、ドメインロジックの複雑さです。

ドメインロジックとはビジネスルール(事業活動の決め事)に基づく計算や判断のロジックです。もちろん、計算や判断にデータを使います。しかし、ドメインロジックの複雑さはデータの扱い方の問題ではなく、計算式や判断条件の複雑さをどのように整理して記述するかの問題です。

集約パターンがわかりにくいのは、本来はドメインロジックの複雑さを整理しクラスの組み合わせで表現する設計パターンである集約を、永続化やデータ整合性という別の関心事とからめて説明しているためです。集約を理解するには、まず、集約を永続化の関心事から切り離して考えたほうがわかりやすくなります。

ロジックの置き場所としてクラスを考える

ドメイン駆動設計を理解し実践するための基本は、オブジェクト指向プログラミングのクラス設計のアプローチです。

オブジェクト指向プログラミングでは、クラスは「データを整理」する手段ではありません。クラスは「ロジックを整理」する手段です。金額・数量・日付・区分値など、ビジネスで扱う値の種類を分類し、それぞれの値に対する計算・判断のロジックをそれぞれのクラスに集めて整理するための手段がクラスです。

クラスは内部に計算・判断に使うデータを持ちますが、それは内部に隠蔽されます。クラスを利用する側が意識するのは内部のデータではなく、操作(メソッド)だけです。

メソッドは、内部に保持したインスタンス変数を使った計算判断ロジックの置き場所です。メソッドがそのクラスに所属している理由は、そのメソッドがそのクラスのインスタンス変数を常に使う必然性があるからです。クラスは内部に保持したデータを操作するロジックを集めるための入れ物です。

ドメイン駆動設計は、このロジックの置き場所としてのクラス、というオブジェクト指向プログラミングのクラス設計の考え方を前提にしています。クラスをロジックの置き場所として設計することがドメイン駆動設計を理解し実践するための基本です。

関心の分離と集約

6章の「ドメインオブジェクトのライフサイクル」にでてくる集約も、まずオブジェクト指向プログラミングの基本である、クラスはロジックの置き場所という視点で理解することが役に立ちます。

集約は、複雑なロジックを複数のクラスの組み合わせで表現する設計パターンです。ひとつのクラスにさまざまな計算判断ロジックを詰め込んでしまうのは「関心の分離」の原則に反するアンチパターンです。

金額に関する計算判断と日付に関する計算判断は別にクラスに分けて整理します。金額も円ベースの価格計算と、円以外の支払い通貨に換算した請求金額の計算があれば、別のクラスに分けて関心事を分離したほうがコードをすっきりと整理できます。

このような役割を明確に分離したクラスを組み合わせて、より複雑なロジックを表現します。複雑なロジックを表現するためにクラスを組み合わせる。これが本来の集約パターンの使いどころです。

集約を設計する時にはデータの整合性や永続化を考えないようにします。ドメインロジックを整理するためには、データの整合性や永続化は異なる関心事です。ドメインロジックの整理にデータの整合性を持ち込むことは、クラス設計を不必要に複雑にします。集約は、ドメインロジックを表現するクラス設計を単純に保つための考え方であり工夫です。永続化の関心事をそこに持ち込んではいけません。

またオブジェクトをできるだけ不変(イミュータブル)に設計することで、そもそもデータの整合性をクラス設計の問題として考える必要性はなくなります。クラスをイミュータブルに設計するのも、クラス設計を単純で分かりやすくするための工夫です。

ドメイン駆動設計の集約とはロジックの集約であり関連の設計である

ドメイン駆動設計』の「5章 ソフトウェアで表現されたモデル」で、最初に取り上げられているのが「関連の設計」です。

5章は、エンティティ・値オブジェクト・ドメインサービスがパターンの形式で説明されていて、ドメイン駆動設計の説明でもこの3つのパターンはよくとりあげられます。一方、「関連」はパターン名として登場していないためかあまり取り上げられることはありません。

しかし集約を理解し活用するには、クラスの組み合わせ方を表現する関連の設計の理解と実践が基本です。関連を考えることが集約の設計の核心です。

5章で登場する集約の具体例

クラス設計では関連の設計はもっとも基本的な設計要素の一つです。エヴァンスが5章を関連の説明から始めているのもそういうことです。

5章では証券取引口座と他のクラスの関連の例が説明されています。

f:id:masuda220:20210507132856p:plain
証券取引口座

このようにクラスとクラスの関係をモデリングしたものが「集約」です。

この集約の例ではどのような計算判断のロジックに関心があり、その計算判断ロジックをどのクラスのメソッドとして提供するかは、まだはっきりしていません。分析と設計が進むにつれ、ビジネス的に重要な計算判断ロジックが特定され、適切な場所にメソッドとしてロジックが配置されていくはずです。

銘柄単位の計算判断ロジックは投資クラスに配置するだろうし、口座番号ごとの合算や評価のロジックは証券取引口座クラスが持つことになるでしょう。

複数の関心事の境界がはっきりしない集約の例

集約の別の例を見てみましょう。

この図は、集約に複数の関心事(複数の計算判断ロジック)が混在している例です。

f:id:masuda220:20210507132924p:plain
ロジックの置き場所の設計例

注文の合計金額と送料計算は、異なる関心事(異なる計算ロジック)です。

図として相互につながっていますが、注文金額を計算するための集約と、送料を計算するための集約とに分けて考えたほうがよさそうです。もちろん、配送先は注文の一部なので、その情報をどうやって送料計算の集約で参照可能にするかという設計課題はあります。この図のクラス設計のイメージのままでは、うまく実装できないかもしれません。

また、送料体系クラスは、送料の計算時に計算の条件によってはデータベースの料金設定テーブルへの参照が必要になるかもしれません。ここらへんになってくると「6章 ドメインオブジェクトのライフサイクル」の議論が関係してきます。

データの操作は別の関心事としてクラス設計からは分離する

しかし、送料を計算するロジックをクラスでどう表現するかを検討する時には、いったんはデータベース参照は別の関心事としてとらえ、クラス設計の一部として考えないようにします。そして、ロジックの表現としてわかりやすい集約のクラス設計ができたら、その後でデータベース参照の設計と実装を検討します。

ドメイン駆動設計あるいはオブジェクト指向プログラミングのクラス設計では、データベース操作の関心事は別の関心事として徹底的に分離するほうが良い設計ができます。 

異なる関心事を同時に考えると頭がこんがらがってしまいます。よい設計はできません。①ロジックの置き場所としてクラスを設計する、②データの記録と参照の視点でテーブルを設計する、③オブジェクトとレコードのマッピング方法の設計と実装を検討する。この3つを別々の関心事として分離して設計することでわかりやすい良い設計ができるようになります。

集約の設計は、ロジックの置き場所の設計として考える

5章の「関連の設計」はクラスの集約の考え方の基本です。そして、エヴァンスのようにオブジェクト指向プログラミングでクラス設計をすることがあたりまえの開発者にとっては、集約の設計がクラスの設計、つまりロジックの置き場所の設計であることをあらためて強調するという発想にはなりづらかったのだと思います。

集約がロジックの整理と表現の手段であることを強調しないまま「6章 ドメインオブジェクトのライフサイクル」の永続化とデータ整合性に関する設計課題として集約を説明したために、本来のクラスの集約の考え方とは異なって説明され解釈されることが多くなってしまったのでしょう。

6章も注意深く読めばドメインロジックの整理に焦点があたっています。タイヤの例は、走行距離に応じたタイヤのローテーションルールの話しです。購入注文の例は、購入注文の承認限度額ルールの話しです。

集約はロジックを整理するためのパターン

クラスの集約の設計は、永続化とは異なる関心事です。クラスの集約はデータの扱い方の設計ではなく、ロジックの記述と整理するための設計パターンです。集約の境界は「あるロジックを表現するために必要なクラスの範囲」として検討します。そういうクラスの集約の設計パターンを理解し実践できている前提で、はじめて6章のオブジェクトのライフサイクルとデータ整合性が重要な設計課題として議論されています。

ドメイン駆動設計』を理解する基礎固めの一つとして、集約をデータ整合性ではなく、ロジックの置き場所の設計として考えてみましょう。

ドメイン駆動設計』の後半の「しなやかな設計」にでてくるシェアパイ演算や「大規模な構造」にでてくるコアドメインとしての経路選択ロジックなどの説明を「集約の設計は計算判断ロジックの整理と記述」という視点で読むことで、ドメイン駆動設計についての理解が深まるはずです。