7つの設計原則とオブジェクト指向プログラミング

設計原則はよい設計をするための指針です。
では、よい設計とはなんでしょうか?

もっとも重要なソフトウェア品質は発展性

ソフトウェアのよし悪しを判断する基準は、大きく分けて三つの観点があります。

  • 合目的性(入出力や計算判断の機能要求を満たす)
  • 安定性(可用性・性能とスケーラビリティ・セキュリティの要求を満たす)
  • 発展性(変更容易性や拡張性)

合目的性と安定性はソフトウェアの利用者が判断可能な外部品質です。
発展性は、ソフトウェアの利用者からは判断しにくい内部品質です。

ソフトウェアの設計品質は、この内部品質です。

目的に適合し安定して稼働しているソフトウェアは価値があります。しかし、内部の設計はボロボロかもしれません。
設計品質の劣ったソフトウェアは変更がやっかいで危険です。修正や拡張のコストが高く、現実的には変更不可というソフトウェアもたくさんあります。

ソフトウェアの発展性がビジネス価値を生む

事業活動はたえまなく変化します。顧客のニーズが変化し、市場での競合関係が変わります。そういう顧客や市場の変化にうまく対応しながら事業を発展させていくために、事業を支えるソフトウェアも変化を続けることが必要です。
「作って終わり」「動いていればよい」ではなく、事業の持続的な成長に貢献できる発展性を持つソフトウェアがビジネス価値を生み出します。

ソフトウェアの発展性が向上すれば、合目的性や安定性も継続的に改善できます。

参考記事:ソフトウェアのもっとも重要な品質は発展性 - ソフトウェア設計を考える

発展性をうみだす7つの設計原則

発展性を向上し、ソフトウェアの変更を楽で安全にするための設計原則には、以下のものがあります。

f:id:masuda220:20200602100546j:plain
7つの設計原則

7つの設計原則は、それぞれが独立した原則ではなく、お互いに強く関連します。
7つの設計原則の中心にあるのがモジュール化の原則です。

モジュール化

複雑なソフトウェアをわかりやすく整理し、変更を楽で安全にするための基本原則がモジュール化です。

モジュールは、簡単にいえば一つのプログラムファイルです。
複雑で大きなソフトウェアを複数のプログラムファイルに分割し、それらを組み合わせて全体をつくりあげるのがモジュール化です。

モジュール化の2つのアプローチ

モジュール化には2つの異なる方向性があります。

ひとつはオブジェクト指向プログラミングの基本である型によるモジュール化です。
もう一つは、「入力-処理-出力」の単位でソフトウェアを分割する手続き的なモジュール化です。

7つの設計原則は、どちらの設計アプローチでも適用可能です。
しかし、型によるモジュール化の設計アプローチと、手続き的なモジュール化の設計アプローチとでは、それぞれの設計原則をどう適用するかの考え方とやり方が大きく異なります。

この記事では、型によるモジュール化(オブジェクト指向プログラミング)の設計アプローチに焦点を合わせて、7つの設計原則を説明します。

モジュール化の2つの設計アプローチの違いを簡単にまとめておきます。

型によるモジュール化

オブジェクト指向プログラミングのモジュール化は型によるプログラムの分割です。

型とは値の種類です。
値の種類ごとに、その値を使った操作(計算や判断)をグルーピングして一つのモジュール(クラス)にまとめるのが、オブジェクト指向プログラミングの基本的な考え方です。
文字列型や整数型を表現したStringクラスやIntegerクラスは、型(値の種類)によるモジュール化の具体例です。

型には二種類あります。
ひとつはStringやIntegerのようにプログラミング言語に組み込みまれた型です。

もう一つは、プログラムの開発者が独自に定義する型です。
アプリケーションで扱いたい値、例えば金額・数量・率・日付・期間・日数などを型として開発者が独自に定義します。

金額や日付を扱うために独自の型をモジュールとして作成し、それらのモジュールを組み合わせてアプリケーションをボトムアップに組み立てていくのが、オブジェクト指向プログラミングの基本アプローチです。

型によるモジュール化は、値の種類ごとに計算や判断の操作をグルーピングしてひとつのモジュールにまとめます。
こうすることで、計算判断のロジックがどのモジュールに書いてあるかをわかりやすく整理できます。そして計算判断ロジックの変更の影響を特定のクラス(モジュール)内部に閉じ込めやすくなります。

手続き的なモジュール化

手続き的なモジュール化は、入力を受け取って結果を出力するまでの一連の処理の流れ(手続き)を一つのモジュールにまとめます。 「入力-処理-出力」という単位のモジュール分割です。

手続き的なモジュール分割はトップダウンのアプローチです。
全体を大きな「入力-処理-出力」のプログラムとして定義し、さらにそのモジュールを「入力-処理-出力」のサブモジュールに分割します。

手続き的なモジュールは、大きなデータクラスを受け取り大きなデータクラスを返す設計が多くなります。

計算や判断のロジックは、受け取ったデータクラスのデータ項目を処理する手順の一部として記述します。 型によるモジュール分割とは対照的に、異なる値の種類に対する計算判断のロジックが一つのモジュールに混在します。

手続き的なモジュール構造では、同じようなデータクラスを受け取れば、同じような計算判断のロジックが複数のモジュールに重複します。
その結果、ソフトウェアの変更はやっかいで危険になります。重複したコードはソフトウェアの発展性を阻害します。

関心の分離

モジュールに分解して組み立てるのは、複雑さを扱いやすくするためです。
しかし、やみくもにモジュールに分解すれば複雑さを単純化できるわけではありません。

わかりやすく扱いやすいモジュールに分解するための設計原則が関心の分離です。
複雑さとはさまざまな関心事が入り組んでいる状態です。関心の分離は、この入り組んだ状態を切り分けて整理するための設計原則です。

 ひとつのモジュールに異なる関心事が混在すると、どこに何が書いてあるかがわかりにくくなります。
モジュールとモジュールの組み合わせ方も、ぎこちなく入り組んできます。
変更をするときに、同じモジュールの別の関心事を記述した個所まで誤って変更してしまうかもしれません。
関心が混在したモジュールを組み合わせると、モジュールとモジュールの関係が複雑なります。あるモジュールの変更が、他のどんなモジュールに影響するか、簡単には把握できません。

では、どのような方針でモジュールを分割すれば、複雑なソフトウェアをわかりやすく整理できるでしょうか?

関心の4象限

f:id:masuda220:20200602094810j:plain
関心の4象限

この図は、関心の分離の基本方針を考えるために、関心事を4つの象限に分けたものです。 横軸では「入出力」と「計算・判断」に関心を分離します。 縦軸では「業務(ビジネス)の関心」と「実装の詳細」に関心を分離します。

入出力と計算・判断の分離

関心の分離のよくある失敗が、ひとつのモジュールに入出力の関心事と計算判断の関心事を混在させてしまうことです。

アプリケーションプログラムは、画面・データベース・通信など外部との入出力を扱う部分と、メモリとCPUを使って行う内部の計算・判断の処理を扱う部分に分離できます。
この二種類の関心事を明確に分離すれば、それぞれのモジュールの役割が明確になり、ひとつひとつのモジュールの記述は単純で扱いやすくなります。

手続き的なモジュール構造は、この入出力と計算・判断の分離がうまくいきません。 入出力の手続きの中に、計算・判断ロジックを埋め込んでしまうためです。

型によるモジュール構造では、計算・判断に使う値の種類、つまり型に注目してモジュールを分割します。
入出力の関心から切り離すことで、計算判断のロジックだけを独立したモジュールにわかりやすく記述できます。
計算・判断の関心事を別モジュールに分離できれば、入出力を扱うモジュールは入出力だけに専念できます。計算・判断ロジックが入らないため、入出力を扱うモジュールを単純化できます。

入出力の関心事と計算判断の関心事を別のモジュールに分離するのが、もっとも基本的な関心の分離です。

業務の関心と実装の詳細の分離

アプリケーションプログラムの目的は業務(ビジネス)の関心事に対して役に立つ機能を提供することです。
そして機能を提供するための手段として実装の詳細があります。

プログラムの多くの部分は実装の詳細の記述です。関心の分離を意識しないと実装の詳細の中に業務の関心事が暗黙的にかつ断片的に記述されがちです。
その結果、業務の関心事をソースコードから読み取りにくくなります。
ソフトウェアの変更要求は、業務の関心事の変更です。変更の理由となる業務の関心事がプログラムのどこにどう記述されているかがわかりにくければ、変更の対象個所を特定したり、変更の影響を把握するのがたいへんになります。

業務の関心事と実装の詳細にかかわる関心事を別のモジュールに分離すれば、それぞれのモジュールの役割が明確になりプログラムの見通しがよくなります。

業務の関心事を表現するモジュールは、実装の詳細から解放され、業務の関心事だけをわかりやすく表現できます。
パッケージ名・クラス名・メソッド名に業務で使われる言葉を積極的に使うことで、業務の関心事とプログラムの記述内容が直接的に対応します。

実装の詳細を記述するモジュールは、プログラミング言語や標準ライブラリに用意された型を使って、どうやってプログラムを実現するかの実装の関心事を記述します。
実装の詳細のモジュール群は、業務の関心事を表現したモジュール群から使われる基礎部品です。

業務の関心事を表現するモジュール群と実装の詳細を記述したモジュール群を分離して、業務の関心事のモジュール群が実装の詳細のモジュール群を使うという関係が明確であれば、どこに何が書いてあるかがわかりやすくなります。
プログラムを変更する時に、業務の関心事と直接対応する変更か、実装の詳細だけに関係する変更かを切り分けがしやすく、変更の影響を狭い範囲に閉じ込めやすくなります。

もっとも複雑な関心事(ビジネスロジック)の分離を徹底する

アプリケーションが複雑になる一番の原因は、4象限の右上の関心事です。 この象限はビジネスルールに基づく計算・判断のロジック、つまりビジネスロジックです。

ビジネスルールとは事業活動を進めるためのさまざまな決め事や制約条件です。
決め事や制約条件が複雑になればなるほど、ビジネスルールに基づく計算・判断のロジックが複雑になり、さまざまな条件分岐や例外的な処理の記述のためにプログラムが入り組んできます。

ビジネスルールの複雑さに起因したアプリケーションのこの複雑さを下げるために効果的なのが、ビジネスロジックを記述するモジュールを、他の3つの象限のモジュールから分離することです。
ビジネスロジックを分離すれば、ビジネスロジック以外のモジュール群は単純になり扱いやすくなります。

では、ビジネスロジックの複雑さそのものは、どうやって整理すればよいでしょうか?
ビジネスロジックの複雑さの整理に威力を発揮するのが、カプセル化と抽象化の2つの設計原則です。

カプセル化と抽象化

カプセル化と抽象化は、型によるモジュール構造でアプリケーションを組み立てるための二本柱です。
特に、複雑なビジネスロジックをわかりやすく記述するためには、カプセル化と抽象化の2つの設計原則が威力を発揮します。

カプセル化

カプセルの意味は「小さなケース」です。*1
カプセル化とは、小さな入れものに何かをギュッと詰め込むことです。

オブジェクト指向プログラミングで、小さな入れ物(クラス)の中にギュッと詰め込むのは、データとそのデータに対する操作です。

例えば、整数データを扱う操作は、加減乗除の4種類の計算と、等しい/等しくない、大きい/小さいという4種類の比較演算があります。
整数データを保持する場所と、その整数データを対象にした加減乗除と比較演算の8つの操作を一つのモジュールにまとめる。これがカプセル化の考え方です。

手続き的なモジュール化では、操作対象のデータは引数として外部から受け取ります。データを保持する場所はロジックを記述してあるモジュールとは別のモジュールです。 つまり、カプセル化をせず、データを保持する場所と、データを使った操作を記述する場所を分けるのが、手続き的なモジュール化の特徴です。

それに対し、オブジェクト指向プログラミングのモジュール化はカプセル化です。データを保持するモジュールに、そのデータを使う計算や判断の操作をいっしょに記述します。

ビジネスロジックカプセル化

カプセル化が威力を発揮するのは、ビジネスルールに基づく計算・判断のビジネスロジックを記述する時です。  

ビジネスロジックは、ビジネスで扱う値(金額・数量・百分率・日付・日数・期間・区分)を対象にした計算や判断のロジックです。
ビジネスで扱う値を使った計算や判断のロジックを、扱う値の種類ごとに一つのモジュールにまとめておけば、どこに何が書いてあるかをわかりやすく整理できます。

金額の足し算・引き算、単価に数量を掛ける計算、百分率を掛けたときの端数の丸め計算、日付の比較、日数の計算、期間内かどうかの判定、... 。こういう計算や判断ロジックを、扱う値の種類ごとに小さな入れ物(カプセル)にまとめてモジュール化します。
こうすることで金額や日付を計算したり判断したりするビジネスロジックがプログラムのあちこちに散らばらなくなります。
この結果、金額に関するロジックの変更が必要な時は、金額クラスだけを考えればよくなります。あちこちのモジュールを調べまわる必要はありません。変更箇所を一つのクラスに特定でき、変更の影響を狭い範囲に閉じ込めることができます。

値の種類ごとに、関連するデータとロジックをひとつのモジュールにまとめるカプセル化は変更を楽で安全にします

手続き的なプログラミングは、カプセル化の設計原則とは正反対のアプローチを採用します。値を保持するデータクラスとロジックを記述する機能クラスを分けます。
データクラスと機能クラスを分けると、あるデータを使う計算・判断のロジックがどの機能クラスに書いてあるのかを洗い出すのがたいへんになります。
一つの機能クラスに記述した判断ロジックを修正しても、同じデータクラスを使っている別の機能クラスの判断ロジックの変更が漏れていた、という事故が起きやすくなります。

抽象化

抽象化とは、さまざまな詳細の中から本質的な特徴だけを選択して取りだすことです。

人間の思考能力には限界があります。いちどに多くのことを考えると、わけがわからなくなります。
ある時点で考えるべきことを重要な本質だけに限定すれば、理解も容易になり、判断もしやすくなります。

モジュールを設計する時も、モジュールのさまざまな側面をいっしょに考えると混乱します。
役に立つモジュールを設計するために、モジュールを抽象化して検討します。
モジュールの本質的な特徴とは何か、そして、モジュールの特徴をどう定義すれば役に立つかを考えるための設計原則が抽象化です。

データ抽象

オブジェクト指向プログラミングのモジュール化はデータの(値の種類)に注目するアプローチです。
アプリケーションで扱うさまざまなデータを、整数・文字列・日付などの値の種類ごとに分類したものが型です。 型(値の種類)ごとにプログラムを分割し、その値を扱うための操作のグループをひとつのモジュールにまとめる(カプセル化する)のが型によるモジュール化です。

データを扱う時に、そのデータを扱う操作に焦点を合わせることをデータ抽象と呼びます。 データ抽象は、モジュールの本質的な特徴は値に対する操作である、という考え方です。 値に対する操作とは、例えば次のようなことです。

  • 整数型の値は、加減乗除・大小比較・等しい/等しくないの演算ができる
  • 真偽型(boolean型)の値は、等しい/等しくないを判定できるが、加減乗除も大小比較もできない
  • 日付型の値は、前後比較・等しい/等しくない・日数の加算・減算ができるが、日付同士の加減乗除はできない

値の操作をモジュールの本質的な特徴と考えるデータ抽象は、モジュールを使う側にとって何が重要かに焦点を合わせてモジュールを設計する、という考え方です。

モジュールをプログラムとして完成させるには、さまざまな詳細の検討が必要です。
しかし次のようなモジュール内部の詳細は、モジュールを使う側にとって本質的な特徴ではありません。

  • モジュールが内部に保持するデータの構造
  • そのデータを使った、計算や判断ロジックの詳細
  • そのモジュールが利用している他のモジュール

モジュールを使う側にとって重要なのは、その型(値の種類)ではどういう操作が利用できるか/できないか、です。モジュールを利用する時の外部の視点からのモジュールの特徴です。

エディタによってはコードの記述を支援する機能として、変数の直後にドット(.)をタイプすると、その変数が参照しているオブジェクトの操作の一覧を表示する機能があります。この操作の一覧がデータ抽象の具体例です。プログラムを書くときのモジュールの本質的な特徴は、そのモジュールで利用可能な操作です。

モジュールを型(値の種類)で分割し、その型が提供する操作に焦点を合わせてモジュールの特徴を定義するのがデータ抽象です。モジュール内部の具体的なデータ構造ではなく、データを操作に抽象化して考えるという設計のアプローチです。

データ抽象は、モジュールの分割方針を、手続きではなく(値の種類)に注目して行う、というアイデアから生まれました。*2 *3
データ抽象、つまり型によるモジュール化オブジェクト指向プログラミングの根底にある考え方です。

ビジネスロジックとデータ抽象

データ抽象の考え方が威力を発揮するのは、関心の4象限の右上の関心事、つまりビジネスロジックを表現するモジュールを設計する時です。

ビジネスルールの実体は、金額・数量・率・区分・日付・日数・期間などのビジネスで扱うデータを使った計算と判断の決め事です。
この計算判断のルールをプログラミング言語で記述したものがビジネスロジックです。

ビジネスルールは、複雑な事業活動を進めるためのさまざまな決め事や制約です。 ビジネスロジックは、このビジネスルールの複雑さをそのまま反映して複雑になります。
ビジネスロジックの複雑さを整理して扱いやすくする方法が、型(値の種類)ごとにモジュールを分割し、そのモジュールの本質的な特徴を、値に対して行う計算判断の操作として定義するデータ抽象の考え方です。

ビジネスで関心のあるデータを、プログラミング言語でどのように扱うかの詳細はビジネスロジックの本質的な特徴ではありません。
ビジネスロジックの本質的な特徴は、どういう値にたいしてどういう計算・判断の操作をしたいか/すべきかです。

例えば金額を扱うモジュールを考えてみましょう。
金額に対して行う操作には以下のものが考えられます。

  • 金額同士の加算と減算
  • 金額と整数の乗算
  • 金額と率の乗算
  • 金額と整数の除算
  • 金額が等しい/等しくないの判断
  • 金額どうしの大小の比較

型(値の種類)によるモジュール化(データ抽象)は、このようにビジネスルールに基づく計算判断を表現するために必要な金額の計算や判断の操作を定義して、ひとつのモジュールにまとめます。
そして値の種類ごとの部品(モジュール)を組み合わせて、より複雑なビジネスロジックを表現するモジュールを組み立てます。

こうすることで、どんな種類のデータに関心があり、そのデータに対してどんな計算や判断を必要としているかを体系的に整理できます。
データの種類に注目して、ビジネスの関心事としての操作に焦点をあわせるデータ抽象のアプローチが、ビジネスロジックを記述するプログラムのモジュール化に効果を発揮します。

手続き的なモジュール構造では、金額データを扱うあちこちのモジュールに計算や判断のロジックが重複しがちです。コードの重複は変更をやっかいで危険にする元凶です。

高凝集と疎結合

高凝集と疎結合はモジュールの質を高めるための設計原則です。

関心の分離・カプセル化・データ抽象の三つの設計原則は、モジュールをどう分割するかの指針です。
分割したモジュールの質を判定するための指針が高凝集疎結合の二つの設計原則です。

凝集度

凝集とは切っても切れない関係の要素の集まりです。

凝集度が低いモジュールは、さまざまな関心事が混在しているモジュールです。
関心事が混在すると、モジュールの意図の理解が難しくなります。
また、ある関心事についての変更の影響が、本来は関係のない部分にも影響し、変更がやっかいで危険になります。

モジュールに格納するデータと操作を一つの関心事に凝集させれば、わかりやすく扱いやすいモジュールになります。
そのモジュールの使いどころの判断と使い方が直感的になります。

どういうモジュールが凝集度が高いかを判断するのは簡単ではありません。ある見方では凝集度が高いと言えるが、別の見方では異なる関心事が混在しているとも言える、ということが起きるからです。

それに対して、明らかに凝集度が低いモジュールは、比較的簡単に判断できます。

次のようなモジュールは、さまざまな関心事が混在した凝集度の低いモジュールだと考えられます。

  • メソッドの数が多い
  • メソッドの引数が多い
  • フィールドの数が多い

メソッドの数が二桁になれば、そのモジュールの凝集度は低いと言えます。データを抽象化した時に、10も20も操作があるのは、複数の関心事が混在している明らかな兆候です。

例えば、文字列型を扱うStringクラスはさまざまな操作が用意されていて便利です。しかし、けして凝集度の高い設計ではありません。 文字列を扱うという広い関心事のモジュールだからです。

文字列の操作は、もっと小さなグループに分けて目的特化のモジュールに分割することが可能です。
文字列を分割するという関心事と、文字列にどんな内容が含まれているかを検査する関心事と、文字列をフォーマッティングしたいという関心事は、切っても切れないほど濃密な関係とは言えません。異なる関心事として別のモジュールに分離できそうです。
高凝集という設計原則を理解し実践できるスキルを上げるには、こういうモジュール分割の可能性を検討して、実際に分割の実験をして効果を検証してみる姿勢がたいせつです。

メソッドの引数が2つを超えたら、凝集度の低下がはじまっています。

オブジェクト指向プログラミングのモジュール(クラス)では、操作の対象の主役は、オブジェクト内部に保持しているデータです。
メソッドに渡す引数が多いということは、メソッドの内部のロジックは、オブジェクトが内部に持つデータよりも外部から渡されたデータに強く関心を持っている可能性があります。 外部から渡されるデータに強く関係するロジックは、そのデータを保持するクラスに移動したほうが、それぞれのクラスの凝集度が高くなります。

フィールドの数が2つを超えたら、凝集度は下がりはじめます。

クラスの凝集度を判断する一つの目安が、フィールドとメソッドとの関係性です。
クラスのすべてのメソッドが、すべてのフィールドを必ず使っていれば、そのクラスの凝集度は高いと言えます。
逆に、メソッドによって使っているフィールドが異なれば、異なる関心事が混在しているクラスです。
特定のフィールドだけと関係するメソッドは、そのフィールドだけを持つ別のクラスとして分離すべきです。
そうすることで、それぞれのクラスの凝集度があがります。

こうやって、凝集度をあげるために、フィールドとメソッドの関係を、切っても切れない関係だけに限定することで、それぞれのモジュールの役割が明確になります。そして、モジュールに対する変更の影響を、そのモジュールの内部に閉じ込めやすくなります。

オブジェクト指向プログラミングの基本プラクティスであるリファクタリングのパターンは、ほとんどが凝集度の低いモジュールを凝集度の高いモジュールに改善するためのパターンです。

結合度

結合度はモジュール間の関係性についての指針です。 モジュール間の関係性は、モジュールのインタフェース(外部に公開された仕様)から判断できます。

判断の目安は三つあります。

  • 小さなインタフェース
  • 少ないインタフェース
  • 理解しやすいインタフェース

小さなインタフェースとは、モジュール間でやりとりされる情報が少ないことです。
凝集度が下がる兆候として説明した引数の数が多くなると、モジュール間でやりとりされる情報が多くなり、それだけ密な結合になってしまいます。

モジュール間の結合の数が少ないほうが、全体の構造はシンプルになります。
モジュール数が同じでも、モジュール間の関係が少ないほどプログラムがわかりやすくなり、また、変更の影響範囲が狭くなります。

あるモジュールを使う時に知っておくべき知識が少ないほど、モジュールの用途が理解しやすく使いやすいモジュールになります。
また、モジュールの誤った使い方が減りプログラムの挙動が安定します。

隠された結合性の問題

表面的には疎結合に見えても、あるモジュールを変更すると、他のモジュールでも同じような変更をしなければいけないケースがあります。

同じような計算式や条件判断のロジックやあちこちのモジュールに重複している場合です。
まったく独立して無関係に見えるモジュールでも、このようなコードの重複があると、あるモジュールに対する変更に関連して、他のモジュールにも同じ変更が必要になります。

コードが重複した複数モジュールへの変更作業は、調べるのも、変更を適用するのも、変更の結果を確認するにも、時間がかかります。このような暗黙の関係性(隠された結合性)はソフトウェアの変更をやっかいで危険にする元凶です。

隠された結合性をなくして、より質の高い疎結合を実現するにはどうすればよいでしょうか?

定義の一点性

暗黙の関係性を取り除くための設計原則が定義の一点性(Single Point of Definition)です。*4

同じ計算式や同じ条件判断は、ひとつのモジュールだけで定義すればコードの重複を防げます。

型(値の種類)によるモジュール化(オブジェクト指向プログラミング)は、この定義の一点性を実現するためのわかりやすいアプローチです。
モジュールを値の種類で分割し、ある値に関係する計算判断を、ひとつモジュールだけで定義するからです。

手続き的なモジュール化の問題のひとつが、この定義の一点性を実現しにくいことです。
手続き的なモジュール構造でも、共通のサブルーチンモジュールを導入すれば、定義の一点性を向上できる可能性はあります。
しかし、入力と出力に注目しトップダウンで手続きモジュールを分割していくアプローチで開発すると、共通のサブルーチンが自然に生まれることがありません。むしろ、同じ計算式や判断条件が、別のモジュールに重複しやすくなります。

型によるモジュール化は、関係する計算判断のロジックを値の種類ごとに一か所で定義することを目的としたアプローチです。
オブジェクト指向プログラミングが、変更を楽で安全にしてソフトウェアの発展性を向上できるのは、型によるモジュール化のアプローチが定義の一点性を実現しやすいからです。

見た目が同じコード

プログラミング言語の記述能力は貧弱です。ですので、異なる意図の計算や判断も、プログラミング言語で書くと同じコードになってしまうことがあります。

見た目には同じコードであっても、プログラムの意図は異なるかもしれません。

定義の一点性というのは、同じ意図のコードを単一定義しようという原則です。 コードの見た目が似ているから共通コードにしようという設計原則ではないことに注意が必要です。

この点でも、型(値の種類)によるモジュール化は重要な設計指針です。
金額と数量は、整数の演算という観点では、内部は似たコードになります。
しかし、金額と数量はビジネスの観点では意味が異なる数値です。金額計算と数量計算は異なる計算です。金額を扱う計算判断と数量を扱う計算判断は、異なる型(値の種類)の操作として別のモジュールで扱うべきです。
そうすることで、関心が分離された凝集度の高いモジュールになります。

7つの設計原則の学び方

設計原則は、言葉として覚えるだけでは役に立ちません。
実際にソフトウェアを開発しながら経験的に、原則の意味と適用のしかたを学ぶことが大切です。

この記事で説明した設計原則を現場で実践するために参考になりそうな、コードの実装例・書籍・ネット上の情報を紹介しておきます。

コードの実装例

型によるモジュール化のアプローチで設計した具体例をgithubで公開しています。

図書館システムの実装例
蔵書の貸出制限ルールや貸出予約の状態管理のルールを、どのように実装するかの具体例です。

オブジェクト指向プログラミングでアプリケーションを開発する場合、オブジェクトと外部のデータ形式(画面・テーブル・JSON)とのマッピングが必要になります。
ドメインオブジェクトをどのように外部形式とマッピングするかの具体例です。
ドメインオブジェクトのマッピング

ドメインオブジェクト設計のガイドライン

この記事の設計原則の考え方をもとに、ビジネスロジックをコードで表現するための設計ガイドとサンプルコードです。

実践ガイドとして使える本

(拙著)現場で役立つシステム設計の原則 ~変更を楽で安全にするオブジェクト指向の実践技法
この記事で書いた設計原則を現場で実践する時に参考になると思います。

リファクタリング 既存のコードを安全に改善する
第2版もでていますが、型によるモジュール化という観点からは、第1版のほうが役に立ちます。

RDRA2.0 ハンドブック 軽く柔軟で精度の高い要件定義のモデリング手法
要件定義、特にビジネスロジックの元になるビジネスルールの発見・整理のための実践的な手法です。

設計の考え方を理解するための本

この記事で紹介した設計の考え方をより深く学ぶための本を紹介します。

エリックエヴァンスのドメイン駆動設計
ビジネスロジックに焦点を合わせる重要性とその効果を著者の実践経験をもとに解説した古典的な名著です。

オブジェクト指向入門 第2版 原則・コンセプト
オブジェクト指向入門 第2版 方法論・実践
型によるモジュール化というオブジェクト指向プログラミングの考え方とそのメリットの詳細な解説です。
また契約による設計(Design by Contract)の考え方も、この本で詳しく解説されています。

ドメイン駆動設計とオブジェクト指向入門の二冊は、こちらの記事でも紹介しています。
masuda220.hatenablog.com

ソフトウェアの発展性を重視する設計の考え方は、以下の本が参考になります。

ソフトウェアアーキテクチャ構築の原理 第2版
ソフトウェア設計をさまざまな視点・観点から俯瞰的に解説した名著です。
特に 28章 発展性パースペクティブはこの記事の根底にある考え方と共通点が多い内容です。

*1:caps:入れ物 + ule:小さいという意味の接尾辞

*2:バーバラ・リスコフたちの研究 A History Of CLU

*3:カプセル化はリスコフがデータ抽象の実現アプローチとして提唱した概念です。情報隠蔽するのではなく、内部のコードを読むことを許容しつつ、内部に依存したプログラムは書かないようにするという考え方です。

*4:参照の一点性(Single Point of Reference)と呼ぶこともあります

ビジネスロジックのモデリングと実装

ビジネスロジックドメインロジック)をどうやってモデリングして、どうやって実装するかの実践例を公開しました。

RDRA 2.0 ハンドブックの図書館システムの実装例 (github)

ビジネスロジックのもとになる業務モデルやビジネスルールのモデリングは、 モデルベースの要件定義手法 RDRA2.0 を使っています。

RDRA 2.0 ハンドブック (Kindle Unlimited会員は無償です)

実装技術は、Java/Spring Boot/MyBatis/Thymeleafを使っています。

JIGという設計の可視化ツールを使って、ソースコードからパッケージ関連図やクラスの一覧を自動生成しています。

JIGリポジトリ

利用方法

アプリケーションの内容

RDRA 2.0 ハンドブックのサンプル「図書館システム」の以下の業務を実装しています。

business-usecase

http://localhost:8080でトップ画面を表示できます。

設計ドキュメント

JIGを使ってソースコードから設計ドキュメントを自動生成します。ドキュメントはbuild/jig ディレクトリに出力されます。
JIGドキュメントの生成には、 Graphviz のインストールが必要です。

実装の範囲

ビジネスルールを中心に実装しています。

RDRA 2.0で可視化した以下のビジネスルールと関連するユースケース・画面・テーブルを実装しています。

  • 貸出制限ルール
  • 予約の状態遷移

貸出業務と貸出制限ルール

composit-usecase-loan

restriction

予約の状態遷移

reservatoin-state

以下の業務は未実装です。

  • 蔵書管理(書籍の注文と蔵書として登録する)
  • 会員管理(会員の登録)

要件定義・仕様化・実装の継ぎ目をなくす開発手法

この図書館サンプルは、以下の考え方とやり方で実装しています。

三層構造+ビジネスロジック

layers ビジネスロジックを独立させるドメイン駆動設計のアプローチを採用しています。

ドメインオブジェクトを中心にしたアプリケーション構造

mapping RDRAの要件定義モデルと実装を、以下のように対応させています。

Spring MVCとMyBatisを使い、ドメインオブジェクトを中心に周辺の外部形式との双方向のマッピングを実現するアプリケーション構造を採用しています。

ドメインオブジェクトのモデル(ビジネスロジックの表現)

model

ソースコードから、JIGで自動生成したドメインモデルのパッケージ構成図です。
このモデルを中心に図書館アプリケーションを組み立てています。

CCSR: Continuous Concurrent Stepwise Refinment

開発手法として、要件定義・仕様化・実装の継ぎ目をなくすCCSR手法を採用しています。

この図書館サンプル実装は、CCSR手法の実践例として開発しています。

ccsr

参考記事: 要件定義・仕様化・実装の継ぎ目をなくすCCSR開発手法

RDRAアドイン

RDRA手法のモデリングツールとして、Enterprise ArchitectのRDRAアドインを使っています。

JIG (設計可視化ツール)

Javaで記述した内容を、俯瞰したり、一覧するためのツールです。

プログラミング言語で仕様を記述するCCSR手法を実践するために必須のツールです。

RDRA - CCSR - JIG 関係図

RDRA-CCSR-JIG

RDRAのモデル要素(左)、三層+ビジネスロジックの構造(中央)、JIGの出力ドキュメント(右)の対応関係です。

ビジネスルール駆動

図の青背景でしめした、RDRAで可視化したビジネスルールをドメインモデルとして実装することが、このアプリケーションの中核です。

RDRA 2.0で可視化された内容と、実装された内容の対応は、以下のJIGドキュメントで確認できます。

  • ユースケース複合図
  • パッケージ関係図(depth4, depth5)
  • 区分図と区分使用図
  • メソッド呼び出し関係図

参考情報

要件定義・仕様化・実装の継ぎ目をなくすCCSR開発手法

ソフトウェア開発の問題点

従来のウォータフォール型で、フェーズ分けと分業を重視し、手続き的なモジュール構造でソフトウェアを開発するやり方には、次の問題があります。

  • 開発、特に大量のドキュメントの作成に膨大な時間と費用がかかる(工程が多く、必要な人員がふくらむ)
  • フェーズ分けと分業のため、関係者の間の認識合わせが難しい(伝言ゲーム)
  • ちょっとした変更でも、調査・修正・確認に膨大な時間がかかる(変更がやっかいで危険)

一方、アジャイルと呼ばれる開発のやり方は、以下の問題が起きがちです。

  • ソフトウェアの規模が大きくなると、どこになにが書いてあるか理解ができなくなる
  • 増築・改築の繰り返しが、全体の構造や品質を劣化させる
  • 全体を俯瞰したり構造を確認するための情報がなく、変更の影響がわかりにくい
  • ちょっとした変更でも、調査・修正・確認に膨大な時間がかかる(変更がやっかいで危険)

どちらのやり方でも 変更がやっかいで危険 になることが大きな問題です。

CCSR手法

CCSR手法は、ソフトウェアの変更が楽で安全になること、ソフトウェアの発展性が最も重要な品質特性である、という考えに基づく開発手法です。

CCSR:Continuous Concurrent Stepwise Refinement (継続的・並行的・段階的な発展)

ウォータフォールでもアジャイルでも実現できなかった、ソフトウェアの変更を楽で安全にするための開発手法です。

コンセプト

  • 要件定義・仕様化・実装の継ぎ目をなくす
  • ビジネスロジックに焦点を合わせる
  • 型によるモジュール構造で組み立てる

特徴

  • 要件定義手法として、要件の関係性可視化・関係者の合意形成を重視したRDRA手法を使う
  • ソフトウェアの仕様の記述に、プログラミング言語(Java)を使い、ツール(JIG)により可視化する
  • アーキテクチャとして三層+ドメインロジックを採用
  • 実装技術としてSpring MVC, MyBatis, Thymeleafを使用

f:id:masuda220:20200527095141j:plain
CCSR手法

従来の開発手法との違いは以下の点です。

  • ソフトウェア仕様の記述に自然言語ではなくプログラミング言語を使う
  • ビジネスロジックに焦点を合わせる
  • 型(値の種類)によるモジュール化
  • 要件定義・仕様記述・実装を双方向でフィードバックしながら全体を段階的に発展させていく

仕様の記述にプログラミング言語を使う

CCSR手法は、ソフトウェア仕様の記述にプログラミング言語を使います。
従来のエクセルなどで大量のドキュメントを作成していくアプローチとは大きく異なります。

仕様記述にプログラミング言語を使うと、仕様と実装の継ぎ目はなくなります。
問題は、要件定義と仕様記述の継ぎ目をなくす方法です。

CCSRでは、要件定義手法のRDRAで可視化した要件をもとに、ソフトウェアの仕様をプログラミング言語で記述し、 仕様や設計のドキュメントは、ソースコードの記述内容から自動生成するアプローチを採用しています。

ビジネスロジックに焦点を合わせる

CCSRでは、ソフトウェアの要件定義と仕様を記述する時に、ビジネスロジックに焦点を合わせます。
ビジネスロジックは、ビジネスルールに基づく計算や判断のロジックです。
ビジネスルールは、事業活動の決め事や制約です。

要件定義と仕様の記述で、ビジネスロジックに焦点を合わせるということは、ビジネスルールに注目し、そのビジネスルールを発見し整理するために、事業活動を学び理解する活動を行うということです。

ビジネスロジックに焦点を合わせると、事業活動を進めるためのさまざまな決め事や制約と、ソフトウェアで表現する計算や判断のロジックが明確に対応します。つまり、要件定義と仕様記述の継ぎ目をなくなります。

型によるモジュール化

型とは値の種類です。
型によるモジュール化とは、ビジネスで関心のある値の種類(金額・数量・率・日付・日数など)ごとに、プログラム単位を分割するということです。
つまり、金額型や日付型というクラスを定義して、金額にかかわる計算判断のロジック、日付にかかわる計算判断のロジックをそれぞれのクラスにカプセル化する、オブジェクト指向プログラミングのアプローチのモジュール化です。

型(値の種類)によるモジュール化も、要件定義と仕様記述の継ぎ目をなくします。
ビジネスで関心のある値の種類を、そのまま型としてソフトウェアの仕様定義に使うからです。
型の定義は、ビジネスルールを記述するための基本の語彙になります。

また、型をクラスとして定義することで、仕様と実装の継ぎ目がなくなります。
ソースコードで型の仕様(外部から可能な操作)を定義すれば、それがそのままクラスとして実装するからです。

CCSR手法を実践する

要件定義・仕様化・実装の継ぎ目をなくすために、ビジネスロジックに焦点を合わせ、型によるモジュール化でプログラムを組み立てるCCSR手法を実践にあたって役に立つ技法・ツール・サンプルコードを紹介します。

CCSRを実現する技法とツール

仕様の記述をプログラミング言語で効果的に進めるために、CCSRでは次のような技法とツールを活用しています。

  1. 要件定義にRDRAを使うことで、整合性が担保され論理的な要件定義モデルが手に入る(仕様記述のインプットの質が高い)
  2. アプリケーション固有の型をどんどん定義(データの抽象化)して、仕様記述の語彙を増やす
  3. RDRAの要件モデルに直接的に対応する設計・実装パターンを用意する
  4. Javaで記述したソフトウェア仕様を図形表現で可視化したり一覧表にして確認できるツール(JIG)を活用する
  5. 要件定義・仕様の記述・実装の一連の活動を、ビジネスルールに焦点をあわせ、ビジネスロジックを軸にアプリケーションを組み立てていく

CCSRの実践例

RDRA 2.0 ハンドブック(Kindle Unlimited会員は 無償) で使われている「図書館システム」を、CCSR手法で仕様化・実装した具体例を、githubリポジトリに公開しています。

図書館システムの実装例

<2020.6.26追記> この実装例を教材にしたチュートリアルが公開されました。
実装の内容を確認したり、ビジネスルールを変更してみるチュートリアルです。
図書館サンプルのチュートリアル

ビジネスロジック中心のアーキテクチャ

CCSRは、ビジネスロジックを中心にアプリケーションを組み立てる開発手法です。

ビジネスロジックの置き場所を独立させ、入出力の関心事から分離します。

f:id:masuda220:20200527101518j:plain
三層+ビジネスロック

ビジネスロジックドメインオブジェクトで表現し、それを中心にアプリケーションを組み立てます。

f:id:masuda220:20200527101614j:plain
ドメインオブジェクトを中心にアプリケーションを組み立てる

ドメインオブジェクトと外部形式のマッピングの実装例も、githubに公開しています。

オブジェクトと画面・テーブル・JSONとのマッピング

CCSR手法の改良と普及

CCSR手法は、さまざまアプリケーション開発で実際に使われ、改良を続けています。

また、ソフトウェア開発組織や技術者個人を対象に、ワークショップや学習の場を提供しています。

CCSR手法に興味があったり、体験してみたい方は、twitter( @masuda220 ) などで、お気軽にご連絡ください。

また、CCSR手法を使って実際に開発している現場に参加したい、とか、CCSRの手法やツールの改善活動に参加したいという方も大歓迎です。ぜひ、ご連絡ください。

参考資料

CCSR開発手法は、記事内に記述したgithubリポジトリ以外に、以下の情報も参考になります。

発展性を重視するソフトウェア開発の考え方

書籍:現場で役立つシステム設計の原則 ~変更を楽で安全にするオブジェクト指向の実践技法

ブログ:ソフトウェアのもっとも重要な品質は発展性 (2020/4/5)

スライド:ドメイン駆動設計 15年の取り組みでわかったこと (2020/3/30)

要件定義手法 RDRA

RDRA モデルベース要件定義手法

YouTube:RDRA2.0のサンプルをJIGで実装してみたよ (2020/4/30)

JIG(設計可視化ツール)

JIGの公開リポジトリ

ソフトウェアのもっとも重要な品質は発展性

 ソフトウェアでもっとも重視すべき品質は「発展性」なんだと思う。 機能要求や非機能要求は、時間とともに変化する。その要求の変化に対応してソフトウェアを発展させていける能力、つまり発展性こそがソフトウェアの価値を大きく左右する。
 発展性に問題があり変化ができないソフトウェアと、発展性に優れ変化と成長を続けやすいソフトウェアの価値の差ということだ。

発展性の価値

 顧客のニーズは変化する。また、市場の競合関係も変化する。そういう事業環境の変化にあわせて、ソフトウェアにも変化を続ける能力が求められている。
 また、顧客のニーズや市場環境の変化がゆるやかだとしても、事業活動をすれば組織は経験を通じて学び成長していく。開発チームに限っても、ソフトウェア開発運用の経験を積むことで、開発の考え方とやり方にさまざまな学びと成長がある。そうやって学んだ知識を適切にかつ迅速にソフトウェアに反映できるほど、事業により多くの価値を提供できるはずだ。

保守性

 従来のソフトウェア品質モデルだと、発展性に似た視点として保守性がある。
 しかし、保守性は発展性とは発想が正反対になる。保守性は文字通り守るための能力である。なにか正しい状態が事前に定義されていて、正しくないことを検出したら、定義済の正しい状態に戻す活動が保守性の基本の発想だと思う。  それに対し発展性は、次の展開を切り開くための能力だ。現在の状態を前提に、よりよい状態を模索しそこに向かって変化していこう、というのが発展性の基本の発想となる。

変更容易性

 保守性も発展性も「変更容易性」として同一視することも可能かもしれない。しかし、保守しようとするのか、発展しようとするのかの方向性の違いは決定的だと思う。保守性を確保するための工夫と、発展性を向上していくための工夫はおのずと異なるものとなる。

発展性を生み出すための工夫

 ソフトウェアの発展性を向上させるために、いろいろやってみた。次の三つの取り組みに手ごたえを感じている。

  • 要件定義・仕様化・実装の継ぎ目をなくす
  • ビジネスルールに焦点をあわせる
  • 型によるモジュール構造を基本にする

要件定義・仕様化・実装の継ぎ目をなくす

 代表的な二つの開発のやり方、ウォータフォールとアジャイルについてソフトウェアの発展性の観点から考えてみる。

ウォータフォール

 いわゆるウォータフォール的な開発手法は、要件定義・ソフトウェアの詳細仕様の記述・実装のためのプログラミングの三つの活動を分断する。
 要件定義・仕様化・実装の三つの活動を分断し、異なる技術者がそれぞれの活動を担当し、活動間の意図の伝達のために、大量のドキュメントを作成する方法を採用する。

要求と実装の乖離(かいり)

 このアプローチで発展性を向上するのは難しい。要求の発生から実装のあいだの工程が多い。時間もかかるし、なによりも、要求と実装との間に距離がありすぎて、要求の変化に対して伝言ゲームを繰り返した結果、要求とはかけ離れた実装になる。コミュニケーションの方向も一方通行で、実装の段階で発覚するさまざまな疑問・仕様の抜け漏れや矛盾についての適切な補足や修正が起きにくい。

手続き的なモジュール構造

 そもそもソフトウェアの変更が構造的に難しい場合が多い。ウォータフォール的な開発では、画面や機能単位に分割した手続き的なモジュール構造を採用する。手続き的なモジュール構造では、ビジネスルールに基づく計算ロジックや判断ロジックを、画面や機能の視点で分割したモジュール群に記述する。その結果、本来はひとつであるべきビジネスロジックが、モジュールのあちこちに断片化しかつ重複する。こういう断片化と重複は、ずさんの設計の結果ではなく、手続き的なモジュール構造を選択した場合の必然的な帰結になる。もちろん、設計が劣悪であれば、結果はさらに悲惨にものになる。
 多くのレガシーなシステムが現在直面している変更がやっかいで危険という問題は、ウォータフォール方式のソフトウェア開発で採用されてきた手続き的なモジュール構造が直接的な原因となっている。

変化の兆候

 ウォータフォール的な開発をリードしてきたSI'erや大手企業の情報システム部門も、いままでのやり方ではうまくいかない。なにか、新たな開発のやり方に取り組んでいかなければいけない、という危機感は少しずつ広がってきているようだ。そういうところからの相談が増えてきた。顧客の要求に対して、いままでのような重厚長大で、時間とコストをかけるやり方では対応ができないことが明らかになってきた。

アジャイル

 一方、いわゆるアジャイルな開発は、活動を分離せず、伝言ゲームのためのドキュメントの大量作成はやらない。ウォータフォールとは正反対のアプローチを模索してきた。しかし、そういうやり方で開発されたソフトウェアも、時間とともにレガシー化が進み、発展性がどんどん劣化してく問題が起きることが明確になってきた。

要件定義や分析設計スキルの軽視と劣化

 アジャイルが提唱され始めたころは、要件定義や分析設計について一定の知識と経験をもった技術者が取り組んでいたと思われる。しかし、アジャイルが広まるにつれ、要件定義や分析設計についてきちんと教育や訓練を受けない技術者が急速に拡大した。また、それほどの分析設計のスキルがなくても、小規模なアプリケーションであれば、それなりに動くソフトウェアを作れるようなフレームワークやその利用のtipsが簡単に手に入るようになったことも、この方向を加速させた。

問題の顕在化

 しかし、アプリケーションがある程度の規模になり、複雑なビジネスルールを扱うことが要求されるようになると、要件定義や分析設計のスキル不足の問題が顕在化するようになった。
 アジャイルというソフトウェア開発のやり方であっても、しっかりとした要件定義やソフトウェア仕様化の知識と経験が重要であることに、多くのアジャイル系の開発者も気づき始めている。

要件定義・仕様化・実装の継ぎ目をなくす工夫

 ウォータフォール的な開発のやり方でも、アジャイル的な開発のやり方でも、発展性を重視するのであれば要件定義・仕様化・実装の継ぎ目をなくすことに取り組むことで大きな可能性が生まれる。

ウォータフォールからの移行

 ウォータフォール的な開発のやり方に習熟した組織であれば、要件定義やソフトウェア仕様化についてのさまざまなノウハウの蓄積がある。問題は分業と一方通行の伝言ゲームである。分業ではなくひとつの技術者チームが要件定義・仕様化・実装を担当するようにすれば、伝言ゲームのためのドキュメントづくりは不要になる。
 要件定義から実装・運用までひとつのチームが継ぎ目なく担当することで、ドキュメントづくりは必要最小限になり、ソフトウェアの生産性は劇的に改善する。しかし、継ぎ目をなくすだけでは残念ながら発展性の向上はそれほど期待できない。手続き的なモジュール構造からの脱却が大きな挑戦テーマになる。(発展性を向上させるモジュール構造への移行については後述する)

アジャイルからの移行

一方、アジャイルな開発スタイルでは、そもそも継ぎ目はない活動を目指してきた。しかし、それは要件定義や仕様化の活動自体が軽視され、経験や知識が足りないという現象を生み出した。アジャイルな方法でソフトウェアの発展性向上にと取り組むためには、まず、要件定義やソフトウェアの仕様化の基本的なスキルをきちんと学び習得することが効果的だろう。アジャイル的な開発だけをしてきた技術者には要件定義や分析設計のスキルが大きな伸びしろになるはずだ。

モジュール構造の移行

 アジャイル的な開発であっても、多くの場合、モジュール構造は手続き的なアプローチのことが多い。アジャイルに取り組んでいるある程度の年齢の技術者は手続き的なモジュール構造で技術を覚えてきた人が多いのが実態だろう。また、Webアプリケーション用のフレームワークでは、手続き的なモジュール構造をフレームワークとして組み込んでしまっているものが多い。ソフトウェアの発展性を生み出すためには、手続き的なモジュール構造から抜け出すことがアジャイル系でも重要な挑戦テーマになる。

継ぎ目をなくす手法とツール

要件定義・仕様化・実装の継ぎ目をなくす手法とツールについては、現在、以下の取り組みをしている

仕様の記述言語と可視化

仕様の記述にプログラミング言語を使うことで、要件定義・仕様化・実装の継ぎ目をなくせる手ごたえを感じている。この仕様の記述にプログラミング言語を使うという取り組みの具体的な内容は、このブログで紹介していきたいと思う。
 プログラミング言語で書いたソースコードだけではわかりにく、全体の構造、クラス定義の一覧、メソッドの呼び出し関係などはJigというオープンソースのツールを使って関連図や一覧表を自動生成している。
 エクセルなどを使って自然言語で記述する詳細仕様よりも、プログラミング言語での記述のほうが、整合性や一貫性が明らかに向上する。また、アプリケーションの関心事に対応する独自の型を積極的に定義することで、ソフトウェアの意図が明確になることがわかった。独自の型をモジュール構造の基本にする点については、後述する。

関係性に注目するRDRAのよさ

 要件定義に使っているRDRA2.0は、要件のさまざまな視点間の関係性に注目して、要件定義の整合性を向上を図る手法である。その結果、要件定義とプログラミングの論理構造との対応がつけやすくなり、要件定義・仕様化・実装の継ぎ目をなくし、ソフトウェアの発展性の向上につながっている。

ソフトウェア開発の複雑さに立ち向かう

 ソフトウェア開発の複雑さを生み出す理由はさまざまあるが、本質的にはアプリケーションの対象とするビジネスの複雑さがその主たる原因だと考えている。したがって、ソフトウェア開発の複雑さに立ち向かうアプローチとしては、ビジネス活動の複雑さ、より具体的には、ビジネス活動を刺激し制約する決め事である「ビジネスルール」の複雑さをあつかうスキルを磨くことが、ソフトウェアの発展性を向上させるための重点課題になる。

ビジネスルールが複雑さの根源

 ソフトウェアの複雑さの根源はビジネスルールにある、というのはエヴァンスのドメイン駆動設計の基本的な考え方である。また、RDRA2.0で取り入れられたビジネスルールの分析とモデル化にも共通する考え方である。

ビジネスルールに焦点を合わせる効果

 ドメイン駆動設計に10年以上取り組み、また、RDRA2.0を使った要件定義に取り組んでみた結果、ビジネスルールに焦点を合わせて要件定義・仕様化・実装を継ぎ目なく行うことが、ソフトウェアの発展性を向上させること、またソフトウェア開発の生産性の向上に大きな改善を生み出すきとにたしかな手ごたえを感じている。

分析設計の知見の言語化への取り組み

 要件定義・仕様化・実装の継ぎ目をなくし、一貫してビジネスルールに焦点を合わせる取り組みの中から、ビジネスルールの分析パターンと実装パターンについて、いろいろな気づきが生まれつつある。ビジネスルールに焦点を合わせた分析設計の実践から生まれつつある暗黙的な設計の知見について、このブログで言語化に取り組んでみようと思う。言語化に取り組むことで自分自身の学びと成長につながることを何度も経験してきている。

モジュール構造の転換の重要性

 画面や機能の単位でモジュール構造に分解する手続き的なアプローチでは、入出力の手順の記述の中に、さまざまなビジネスルールに基づく計算や判断を記述することになる。このアプローチの問題点は、ひとつのビジネスルールのソフトウェア表現が、断片化し重複しやすいことだ。またビジネスルールがif文などに暗黙的に埋め込まれがちだ。

型によるモジュール化

 ソフトウェアの発展性を向上させるには、モジュール構造の考え方を抜本的に変えることが必要である。具体的には、型(値の種類)ごとのモジュール構造を、基本とすることである。  この考え方は、前述の「ソフトウェアの複雑さはビジネスルールの複雑さに起因する」という考え方と密接に関係する。

ビジネスルールを型を使って表現する

 ビジネスルールとは事実を表現する値(データ)に基づき、計算や判断によって、なんらかの結果(導出した事実)を生み出す決め事だといえる。このビジネスルールに関わる計算・判断のロジックを一元的に記述し、一貫性を持たせるための工夫が「型によるモジュール構造」である。

型はビジネスルールを明示する

 より具体的にはビジネスに登場する様々な値の種類(金額、日付、期間、区分、カテゴリ、..)を、分類・整理し、それぞれの値の種類を独自の型として定義し実装する、というアプローチである。
 独自の型を定義することで、その型の名前がビジネスルールを表現する豊かな語彙(ごい)になる。独自の型を積極的に使うことで、ソースコードがビジネスルールを具体的に語り出すようになる。どこに何が書いてあるかが明快になることはソフトウェアの発展性を生み出す力になる。

オブジェクト指向プログラミング

 値とそれを使った計算や判断ロジックを一つにまとめて独自の型として定義するのは、まさにオブジェクト指向プログラミングの根本にある発想である。さまざまなビジネスルールで扱う値を分類・整理して、その結果を独自の型として定義しプログラミンしていく技法についてもいろいろなパターンが明らかになってきた。ビジネスルールの表現にオブジェクト指向プログラミングを活用するやり方についても、このブログで紹介していきたいと思う。

 継続的・並行的・段階的な発展(CCSR)

 この記事で書いたソフトウェアの発展性を重視した開発のやり方に、CCSRという名前をつけた。

  • Continuous 継続的
  • Concurrent 並行的
  • Stepwise 段階的
  • Refinement 発展

の略語(頭字後)である。

CCSRへの取り組み

 ソフトウェアの発展性は、事前の設計や要件として作りこむ性質ではない。顧客ニーズの変化、ビジネスニーズの変化、事業組織や開発チームの学びと成長に合わせて、継続的に、さまざまな活動を並行的に、そして一歩ずつ段階的にソフトウェアを発展させていく取り組みが生み出す能力である。CCSRという体系化は、まだ仮説と検証の初期の段階である。しかし、個々の要素は、私自身のさまざまなソフトウェア開発経験の中で会得してきた工夫であり技法である。そしてCCSRとしての体系化に取り組み始めたこの半年で、初期の検証結果として、十分な手ごたえを感じている。

ともに取り組む

 CCSR手法のついて、より多くの人に知ってもらい、可能であればそれぞれの現場で挑戦・評価してもらい、お互いの知見を共有できることを願って、ブログとして言語化に取り組んでみた。イベントなどの登壇の機会があれば、そちらもでCCSRの言語化と紹介を積極的にやっていこうと思う。

興味のある方は、ぜひ、いっしょにCCSR手法を取り入れたソフトウェア開発に取り組んでいきましょう。

モダンなSIer開発手法 勉強会

昨日、株式会社ディマージシェアさん主催の モダンなSIer開発手法 勉強会 - connpassでお話した内容です。

発表に使った資料

要約

  • 発展性に焦点をあわせる(プロジェクト型からプロダクト型へ転換する)
  • オブジェクト指向ソフトウェア構築の考え方(型によるモジュール化)を基本にする
  • ドメイン駆動設計の考え方(ドメインロジックに焦点を合わせ、コアドメインに集中する)を取り入れる
  • 要件定義・仕様・実装を並行して行う
  • 仕様の記述にプログラミン言語(Java)を使う
  • ソースコードから仕様を可視化する
  • 段階的に継続的に品質を向上させる

補足

ソフトウェア開発の複雑さを多面的に捉える

ソフトウェアシステムアーキテクチャ構築の原理(第2版)の枠組みで考えてみる。

7つの視点

  • 合目的性
    • コンテキスト
    • 機能
    • 情報
  • 構造性
    • 開発
  • 実行性
    • 並行性(実行モデル)
    • 実行環境
    • 運用

合目的性を、RDRA2.0で要件定義をする
構造性を、型によるモジュール化で実践する現場で役立つシステム設計の原則

  

仕様をJavaで記述し、設計を可視化する

Jig : 設計可視化ツール
Jig チュートリアル

オブジェクト指向プログラミングを学ぶための推薦図書

オブジェクト指向プログラミングを学ぶ

オブジェクト指向プログラミングという言葉は、広い意味で使われている。 オブジェクト指向プログラミングをキーワードにすべての情報をかき集めて理解するというアプローチは現実には無理。 目に付いた重要そうなところを見繕って集めてみても、たぶん混乱するだけ。

この記事では、オブジェクト指向プログラミングのいろいろなアプローチの中で、

  • クラスを使って独自の「型」を定義するプログラミングスタイル
  • 関連するデータとロジックをまとめて、小さな入れ物に格納する「カプセル化」を重視するプログラミングスタイル

を学ぶための参考図書を紹介したい。

型とカプセル化に重点を置く設計スタイルがわかってくると、それとは異なるスタイル、異なる力点を置くアプローチとの違いが具体的にわかるようになってくる。*1 *2

まずは、オブジェクト指向プログラミングの中で、型・クラス・カプセル化に力点を置く設計スタイルを身につけることを現場で役立つ実践的な学び方として推奨したい。

型とカプセル化を重視したスタイルを学ぶための三冊

以下の三冊は、必携書。すべてを読み切り理解することはできないかもしれないが、手元において少しずつ身に着けていきたい古典的名著。

読み方のヒント

どの本も全部を順番に読んでいくというより、ざっと全体を眺めたら、とりえあず、要点をみつくろって読むのが良い。基本は拾い読み。 あとは、何かの機会に、少しずつ、拾い読みの範囲を広げたり、拾い読みしたところを、なんども読み直してみる。

リファクタリング」の読み方

「第3章 コードの不吉な臭い」に列挙されている臭いの最初の6つ(「変更の分散」まで)を、重点的に読む。そして、その中に登場する、リファクタリングパターンだけは、一通り読むようにする。同じパターンが何度もでてくるので、何が、リファクタリングの基礎的な技法かが理解できる。

それだけでも、だいぶ、クラスの使い方とかカプセル化の感じがつかめるようになってくる。

ドメイン駆動設計」の読み方

読みにくい本です。まず、その前提で拾い読みする。

すぐに理解できるかどうかは別として、この本の根底にある考え方や、何に重点を置いているかに触れるための、最初の必読個所は、以下。

  • まえがき
  • 第9章 暗黙的な概念を明示的にする
  • 第15章 蒸留
  • 結論

ドメイン駆動設計という設計スタイルが、何を重視しているかに触れるには、この4か所を、ぜひ読んでほしい。すべてが理解できなくても、エヴァンスのこだわりポイント、設計スタイルがよくでている個所。

エヴァンスの考え方の特徴は「ある重要な部分に集中する」ことを重視している点。全体を同じ設計スタイルで均一に仕上げる、という発想とは正反対。アプリケーションソフトウエアの全体に影響を及ぼすのは、ドメインロジックであり、ドメインロジックの中でも中核の関心事と周辺的な関心事がある。中核のドメインロジックの設計と実装に焦点を合わせることで、ソフトウェア全体の構造と秩序が保たれ、さらなる発展が可能になる、という考え方。

この設計スタイルを支える技法の説明としては、次の2つの章が基本。

  • 第5章 ソフトウェアで表現されたモデル
  • 第10章 しなやかな設計

ただし、この2つの章は、必読ではない。オブジェクト指向プログラミングの基本的な技法の話なので、型とかカプセル化が理解できていれば、改めて、この2つの章を読み直す必要はない。 この2つの章が「わけがわからん」と思ったら、値オブジェクトを具体例で解説した情報や、リファクタリング本にあたったほうが良い。

なお、エヴァンスの書き方は、章のはじめでわけのわからない(?)たとえ話から始まることが多い。なんのたとえ話か最初はわからず混乱してしまうのが、多くの人が陥るワナ。私もそうだった。最初は、たとえ話は読み飛ばすくらいでよいと思う。後ろの内容が理解できて、はじめて意味がわかる例え話が多い。

もう一つ注意点。 クラス図やデータベースが登場する個所を、データモデリングの話として読んでしまうことも、わりと多くの人が陥るワナ。データモデリングや手続き的なアプリケーションプログラミングができる人ほど、このワナに陥りやすい。そう読もうと思えば、そう読むこともできる。まあ、そういう視点から読むと、エヴァンスの書いていることは稚拙というか的外れというか、なんじゃこりゃ、という評価になるんだと思うけど。

「型」や「カプセル化」の話なんだ、ということをいつも意識して読んだほうがエヴァンスの伝えたいことをすなおに読み取ることができる。 たとえば、最初はメソッド定義のないクラス図の個所は読み飛ばすべきだし、データベースと関連づけて説明されている個所も読み飛ばしたほうが良い。 関心の焦点はあくまでもドメインロジックをどうやって独自の型で定義するか、そして、独自の型の定義にどんな意図と効果があるか、ということを読み取ることに焦点を合わせたい。

オブジェクト指向入門」の読み方

分厚いし内容も難解なので、読破しようなどとは考えないほうが良い。 しかし、オブジェクト指向プログラミングの考え方を学ぶには、この本には有用な情報が詰まっている。 契約による設計とか、コマンドとクエリの分離など、良く知られた設計原則は、この本で提唱されている内容。

著者のバートランドメイヤーさんは、ご本人がおっしゃっているように「分類マニア」。とにかく、品質の分類、判断基準の分類、継承の分類、... など、分類好き。テーマごとに細かく分類し、それをひとつずつ解説するものだから、この厚さになってしまう。 だから、基本的には、「ああ、また分類病がはじまったな」くらいの感覚で、最初はちょっと距離を置いて、全体をぼんやり追いながら、全体としてどんなことが書いてあるかを掴むことから始めるとよいと思う。

この本を隅から隅まで精読する必要はないと思うけど、

  • ソフトウェア構築の基本はモジュール性
  • オブジェクト指向は型によるモジュール化を選ぶ
  • 型をカプセル化するクラスが唯一のモジュール単位であるべき

というメイヤーの考えを知って損はない。 同意するかどうかは別として。

拾い読みするとしたら、重点ポイントは、以下。

  • 第3章 モジュール性
  • 第5章 オブジェクト技術への道
  • 第11章 契約による設計:信頼性の高いソフトウェアを構築する
  • 第17章 型付け
  • 第22章 クラスの見つけ方
  • 第23章 クラス設計の原則
  • 第27章 オブジェクト指向分析
  • 第28章 オブジェクト指向構築過程
  • 第29章 オブジェクト指向という手法を教える

拾い読むといってもちょっと多いかな。まずは、何が書いてあるかを知るには、ざっとこのくらいは目を通しておきたい。 「継承」について書いてある章は意識的に割愛してあります。メイヤーさんは、熱烈な実装継承押しなんですが、私は、逆に実装継承は、やらない派なので。 リスコフは、実装継承はカプセル化の原則をやぶるため好ましくないであろう、と予測していた。そして、その後の開発現場での実践でその予測が正しいことは、すでに実証された。つまり、実装継承はやめたほうがよいよ、という立場です。

もっと読みやすい本が欲しいよね

ということで、書いたのが、拙著。

現場で役立つシステム設計の原則 ~変更を楽で安全にするオブジェクト指向の実践技法

特に、以下の4つの章で、型とカプセル化の考え方をできるだけわかりやすくコードの具体例で説明してみた。

  • 1章 小さくまとめてわかりやすくする
  • 2章 場合わけのロジックを整理する
  • 3章 業務ロジックをわかりやすく整理する
  • 10章 オブジェクト指向の学び方と教え方

この本は、「Java言語入門」的な本と、難解な「ドメイン駆動設計」との間を埋める本が求められているのでは、という企画から生まれた本。 最初に話をもらった時、私自身、そういう間を埋める本があったらいいな、と思っていたので二つ返事で引き受けた。 出版できるまでは、いろいろあったんだけど、なんとか世に送り出すことができた。 おかげさまで、出版から2年たった今でも地味に売れ続けているので、それなりの評価はしてもらえていそう。 「若手にススメた」みたいな話を時々目にするので、本の企画意図通りの読まれ方がされているのかな、とうれしく思っている。

この本を書く時、「継承」「カプセル化」「ポリモーフィズム」「型」とか、いう言葉はださずに、コードの書き方の工夫に振り切って、なぜそうすることのメリットがあるかをできるだけ、わかりやすく書こうと思った。

わかりやすさという点では、それなりの結果がだせたのではと自負している。 まあ、そういうわかりやすさが気に入らない人もいるんだなあ、ということもわかった(笑)。役に立つことが書いてあるけど、書き方が悪い、というコメントを見たとき、役に立つことは伝わったんだよね?、伝わったのに書き方が悪いってどういうこと?という感じで、とても不思議だった。

あと、現実的ではないとか、実際には導入できない、というコメントもいくつか見かけたけど、「自分が実際にやって、それなりの結果を出してきた内容だけ」を書いたので、うーん、実際にやっているんですけど、という感じなんだよね。逆に言うと、例えば国際化対応とか、テスト駆動開発とか、私自身が実際にやっていないことは、何も書いてないんですよね。

まあ、わかりやすいので、読む人の持っている設計スタイルとの干渉が強くて、そういう反応もあるんだろうな、と思っている。何が書いてあるかは、それなりに伝わっているんだろうなあと。

今、読み直してみると、もうちょっと「型」と「カプセル化」という用語の説明をどこかでしたほうが良かったかなと思う。 あと「ポリモーフィズム」も、多重定義・総称性・部分型の三種類あることの説明と、それぞれのポリモーフィズムについてちゃんと説明してもよかったかもしれいない。

この本は、ドメイン駆動設計への入門書的な意味あいは確かにある。ドメインモデルを中心に設計することの意味とその効果について、そうとう意識して書いた。

ドメイン駆動設計と並んで、私が意識していたのは、バートランド・メイヤーのオブジェクト指向入門。 メイヤーさんは、オブジェクト指向はプログラミング技法にとどまらず、ソフトウェア全体の構造と秩序、ソフトウェアの開発プロセス、設計の学び方や設計の知見の共有と流通まで、ソフトウエア開発のさまざまな側面が大きく変わる革命なのだ、ということを力説している。 私自身、型とカプセル化を重視する設計スタイルを現場でいろいろ実践する中で、ソフトウェア開発のさまざまな側面に影響がでることを日々実感している。9章 オブジェクト指向開発プロセス、10章 オブジェクト指向の学び方と教え方の2つの章は、そういう現場で得た知見を元に、開発のやり方が変化しつつあること、型とカプセル化を重視する設計スタイルのわかりやすい教え方について、言葉にしてみた。

まとめ

後半は、自著の説明になっちゃったけど、独自の型を定義し、ドメインロジックをカプセル化する設計スタイルを学ぶ入門書としては、お役にたてるのではと思っています。

私の本の内容がものたりなかったら、ぜひ、前半でとりあげた三冊を手元に置いて、最初は拾い読みでよいので、読んでみてください。 型とカプセル化にこだわるオブジェクト指向プログラミングがわかってくると、世の中のさまざまなオブジェクト指向に関する情報の関係性(と無関係性?)が、いろいろ見えてくると思います。

*1:アプローチの違いは、こちらにわかりやすい説明と、参考情報へのリンクがあります→オブジェクト指向とは何ですか

*2:こちらも大きな流れとして参考になります→新人プログラマに知っておいてもらいたい人類がオブジェクト指向を手に入れるまでの軌跡

ドメイン駆動設計を理解する3つのキーワード

ドメイン駆動設計との出会い

10年前に、エヴァンスのドメイン駆動設計を初めて読んだ時は、書いてある内容がほとんど理解できなかった。 あまり、面白いとも思わなかった。

当時は、現場でバグだらけのコードと格闘していた。障害が報告されるたびに、リファクタリング本を参考に、該当個所の長いメソッドや大きなクラスを片端からリファクタリング。その結果、コードがわかりやすくなり、やっかいなバグが単純な修正で解消できてしまうことの効果に驚き、設計の重要性を再認識していた。

それ以前は、UNIXC言語OraclePL/SQLという、オブジェクト指向ではない世界で技術を身に着けてきた。 どちらかというとオブジェクト指向には、ネガティブな印象を持っていた。現場では役に立たんだろうと。

バグとの格闘の中で、リファクタリング(設計改善)の威力を肌で感じ、その考え方とやり方がオブジェクト指向に由来するということを知るにつけ、オブジェクト指向も食わず嫌いではなくもう少し学んでみようと思った。

ワーフスブラックの「オブジェクトデザイン」とバートランドメイヤーの「オブジェクト指向入門」を買い込んで読み始めてみた。 両方とも面白いと思うところもあったが、何を書いてあるのか、よくわからないところも多かった。

実践的なオブジェクト指向との出会い

大きな転換点は、ケントベックの「実装パターン」を読んでからだった。

オブジェクト指向とは何か、という説明ではなく、「変更の影響の局所化」「コードの重複の除去」「ロジックとデータの一体化」「宣言型の表現」などの設計原則を、なぜそうするのか、具体的にどうやるのかが、わかりやすいサンプルコードで説明されていた。

特に「実装パターン」の中の「Value Object」は、大きな転機になった。 この転機については、こちらの記事でも書いた。 masuda220.jugem.jp

「実装パターン」を何度も読み直し、いろいろ実践した後で、改めて「ドメイン駆動設計」、「リファクタリング」、「オブジェクト指向入門」、「オブジェクトデザイン」を読み直してみたら、それぞれの本の内容がだいぶわかるようになってきた。 特に「ドメイン駆動設計」の現場でのエヴァンスの設計に奮闘するさまざまなエピソードが、生きた事例として理解できるようになってきた。

その後、Value Object 以外にも、「ドメインを隔離する」「アグリゲート」「リポジトリー」「境界づけられたコンテキストとモデルの整合性パターン」などに関心を広げ、現場での設計に取り入れるようになった。

その結果、コードが整理され、エヴァンス本が目指す「進化と成長を続けるソフトウェア」というものがどういうものか、ある程度、理解できるようになった。ドメイン駆動設計は、ソフトウェアの変更を楽で安全する技法として実践的で効果的だという手ごたえを感じるようになった。

もやもや感

そういう手ごたえもあって、現場で実践するだけではなく、ドメイン駆動設計について、ブログを書いたり、イベントで登壇したり、他の現場に出かけて意見交換や助言をしたり、本を書いたり、いろいろアウトプットをしてきた。

アウトプットしながら、新たな気づきがあり、また次の実践とアウトプットということを繰り返してきた。

しかし、どうもすっきりしないもやもや感も抱えていた。 簡単に言えば、表面的形式的には実践できているかもしれないが、本質をとらえ切れていない、あるいは、本質を言語化できていない、という違和感。

現場でのソフトウェア開発の中で、エヴァンス本にでてくるブレークスルー的な手ごたえをいくども経験できたが、偶然感がつよく、もっと意図的にそこに近づく道があるのでは、という漠然とした予感めいたものもあった。

三つのキーワード

去年の夏に、あるイベントのワークショップ用に資料を作っている時に、その違和感を解消する手がかりをつかんだ。 ドメイン駆動設計と他の設計方法を比較して説明するスライドを作っていた時だった。

その資料と作った時点では、まだうまく言語化できていなかったが、それから数カ月、自分なりに手がかりを、なんとか言語化しようと試みてきた。 イベントやワークショップで、実際にいろいろなキーワード(の候補)を使って説明し、その反応を受けとめる中で、三つのキーワードが中核の概念として明らかになってきた。

それが次の三つのキーワード

  • ビジネスルール
  • 計算モデル
  • 型指向のプログラミング
ビジネスルール

エヴァンス本に繰り返しでてくる「ドメインロジック」「ドメインの知識」「ドメインの理解」などの言葉は、具体性に欠ける。 対象が広くぼんやりとしている。

ドメインロジック」をビジネスアプリケーションの分野に限定すれば、ビジネスロジックとなる。 そして、ビジネスロジックはビジネスルールとほぼ同じ意味になる。

ビジネスルールとビジネスロジックを区別するとすれば、ビジネスルールは、プログラムで表現するかしないかとは無関係な、実際のビジネスの活動の中の取り決めや約束事。ビジネスロジックは、ビジネスルールのある部分をプログラムで表現したもの。

いずれにしても「ドメインロジックに焦点を合わせる」は「ビジネスルールに焦点を合わせる」と捉えることができる。 実際のところ、エヴァンス本にも「ルール」という言葉は300回近く登場する。「ポリシー」や「制約」という言葉も含めれば、ドメイン駆動設計の重要な関心事の一つが「ビジネスルール」にあることはまちがいない。

ドメインロジック」を「ビジネスルール」と置き換えてみると、エヴァンスの意図するところが、より具体的に理解できるようになる。

ドメインの知識とは「ビジネスルールの知識」であり、ドメインエキスパートとは「ビジネスルールについての知識と経験が豊かな人」であり、「ドメイン層」はビジネスルールを記述する場所、というわけだ。

ドメイン駆動設計の考え方を理解し実践するカギは、「ドメインロジック」を「ビジネスルール」という、より具体的な対象としてとらえることだ。

計算モデル

ドメインモデル」も、あいまいでわかりにくい言葉だ。

エヴァンス自身が書いているように、ドメイン駆動設計でいうところの「ドメインモデル」はオブジェクト指向のコミュニティで生まれた設計の考え方の一部である。 オブジェクト指向も、いろいろな解釈がある。私は、データモデルへの対比という意味も含めて、オブジェクト指向のモデルとは「計算モデル」だと思っている。

実際に、エヴァンス本に登場するドメインモデルの例は、次のような計算モデルである。

  • 投資シンジゲートの配分比率計算(シェアパイ)
  • コンテナへの荷物の格納ルール
  • 輸送経路を自社に有利にするための経路選択のバイアスポリシー
  • プリント基板の配置ルールを基にした部品配置と配線の妥当性の判定

ドメインモデル」は、なんらかのルールに基づいた「計算と判定のモデル」ととらえると、だいぶすっきりする。

アプリケーション全体としては、データを永続化したり参照したりする仕組みが必要になる。しかし、「ドメインモデル」だけに注目すれば、データの記録と参照からは隔離した、純粋な「計算モデル」という理解がしっくりくる。

「計算モデル」を実行するには、計算の準備としてデータの参照が必要だし、計算結果を記録することも必要だろう。しかし「計算モデル」を、データの参照と記録から意図的に分離することで、「計算モデル」はプログラミングの対象として、よりわかりやすく、扱いやすくなる。

エヴァンス本では、リポジトリや集約の説明のところで、データの永続化の問題が登場するために、この「ドメインモデル=計算モデル」という図式が、ぼやけてしまっている。しかし、全体を読めば、ドメインモデルは「計算と判定のモデル」であり、「データの記録と参照」からは隔離されたものとして扱われていることがわかる。

リポジトリは計算モデルを入出力の関心事から分離する仕組みであり、集約(アグリゲート)はデータの集約ではなく、「計算の集約」を表現する手段だということだ。集約が維持するのは、データベース上のデータの整合性ではなく、計算の整合性であり一貫性ということだ。

型指向のプログラミング

計算モデルをプログラミング言語で表現する場合、例えばFORTRANのような手続き型の言語でももちろんできるが、エヴァンス本が採用している実装技法は、オブジェクト指向である。 エヴァンス本人が、あるイベントで述べているように、本を書いた当時は "I loved Object Oriented." 、 現在は "I still like it" と続けている。温度感は変わっているが、オブジェクト指向が基本であることは変わっていない。

問題は「オブジェクト指向」といってもいろいろなとらえ方があること。 ここらへんの詳しい話は、以下の説明が必読。

jp.quora.com

簡単に言えば「メッセージパッシング」で代表されるオブジェクト指向と、「型」で代表されるオブジェクト指向があり、両者は本質的に異なるアプローチであるということ。 エヴァンス本を読むと、若干の混乱があるが、基本的には「型」を基本とするオブジェクト指向と理解してまちがいないだろう。

ビジネスルールを「計算モデル」ととらえ、計算モデルをコードで表現するには、「型」に基づくオブジェクト指向プログラミングが役に立つ。

「型」とは、計算の対象となる「値の種類」であり、有効な値の範囲と、その値を使った計算を定義したもの。 計算モデルの表現技法として、「型」中心のプログラミングがぴったりというわけだ。

ビジネスアプリケーションであれば、金額、数量、日付、日数、期間などの値の種類を、それぞれ独自の型として定義し、型ごとに、有効な値の範囲と、必要な計算を定義する。

BigDecimal型やLocalDate型は、有効は値の範囲がとてつもなく広く、可能な計算も多岐にわたる。ビジネスの計算モデルを記述するには、焦点がぼやけてしまう。また、ちょっとした記述のミスでバグが混入する可能性が大きくなる。

その問題領域の計算モデルにあわせて、厳密に値の範囲を制限し、必要最小限の計算だけを定義した独自の型を用意することで、プログラムがみちがえるようにわかりやすく安全になる。

エヴァンス本の、配分比率の計算、コンテナの格納条件の判定、経路選定のバイアス計算などは、型指向でプログラミングする具体例になっている。暗黙の概念を明示したり、制約を表現したり、設計の意図を明確にし、変更を楽に安全にする技法として、クラスを使って型を定義するプログラミングスタイルを採用しているわけだ。

実際、価格計算とか日付計算がともなうビジネスルールでは、型指向のプログラミングで記述することで、ロジックを一か所にまとめてわかりやすく整理できる。

値を「型」として扱う設計パターンの代表であるValue Object パターンは、ビジネスルールの計算モデルを実装するための、基本であり最強の道具といってよいだろう。

実験は続く

「ビジネスルール」「計算モデル」「型指向のプログラミング」

この三つのキーワードでドメイン駆動設計を解釈し、実践してみる実験は、まだはじめたばかり。

自分としては、そうとう手ごたえを感じているけど、実際のコードでもっともっと実験し小さな失敗を繰り返しながら、知見を増やし、こうやってブログで書いたりイベントで登壇したり、いろいろな現場で設計に関心とエネルギーを持つ技術者たちと意見交換をすることで、さらに多くの学びができそうな予感にわくわくしている。

参考情報

ビジネスルール・計算モデル・型指向のプログラミングで作ってみたサンプルアプリケーション github.com

このサンプルアプリケーションを解説したときに使った資料

www.slideshare.net

型指向のプログラミングの設計ガイド: 設計ガイドライン · masuda220/business-logic-patterns Wiki · GitHub

参考書籍: gihyo.jp