見出し画像

【Javaでドメイン駆動設計を実現する-2】三層アーキテクチャとドメインモデルの分離

これのつづき。

三層アーキテクチャって何だったっけ

これ。

この例ではレイヤーが「四層」になっていてドメインが既に分離されてるけど、元々は「Presentation+Application+DataSource」の三層構造が一般的だったそうで。

ところが、三層構造で作っていくと、Application層にうぞうぞと業務ロジックが増えていって、結局変更に弱いアプリケーションになってしまう。

ので、そこから業務ロジックを分割したドメインモデルを新たにつくり、三層構造と業務ロジックを分離していこう、というのが今回のお題。

業務ロジックをべりべり分離してドメインモデルを作ろう

データを持つクラスに、そのデータに関連する処理をどんどん集めていき、「オブジェクト指向らしい」設計にする。(「データだけ持つクラス」はオブジェクト指向らしくない)

・↑のためには、「自分が持っているデータをそのまま他のクラスに渡すクラス」を作らないようにする。

使う側のクラスにロジックを書き始めたら立ち止まる(getValueした値を足したり引いたり、ifとかforとか使い始めたら注意する)。そのロジックは使われる側のクラスに書いた方がいいのでは? と考える。

ドメインモデルの考え方でシステムを設計しよう

まずは、業務マニュアルを読んだり、担当者からの聞き取りなどを通して「分析」と「設計」を行う。

人間のやりたいことを正しく理解する=分析

分析した「やりたいこと」を、実際に動くソフトウェアとして実現する方法を考える=設計

分析と設計は同じ人がやらないと、「やりたいこと」が正しく実現できない。また、分析するときに「このアプローチで、本当に実際に動くプログラムになるか?」を意識しないと、分析結果と設計が乖離してしまって複雑なプログラムになってしまう。

分析と設計に当たっては、クラス図や業務フロー図などを使って、まずは業務全体を俯瞰的に捉えていく。

ヒト・モノ・コトに注目してドメインオブジェクトを見つける

ヒト……業務活動の当事者。意志があり、判断をする。→対応するオブジェクトは、判断・加工・計算のロジックを持つはず

モノ……ヒトが業務を遂行する時の関心の対象。物理的なモノのほか、権利や義務などの概念もモノとして扱う。→対応するオブジェクトは、数量や状態、日時や期間、位置などの情報と、それらの情報をどう判断・加工するかのロジックを併せ持つはず

コト……ヒトがヒトやモノに対して「何かしらの判断や処理」を行った結果発生した事象の全て。「注文が入った」「発送した」「入会した」「退会した」「キャンセルされた」など。

業務アプリケーションの場合、「コト」の管理が関心の中心なので、「コト」に注目して全体の関係を整理していくと見通しが立ちやすい。

「コト」は大抵起きる順序が決まっている(受注→出荷→請求→入金、入会→退会、など)ので、業務フローを整理しやすく、またその「コト」に関わるヒトやモノも限定されてくるので、コトを軸にヒトやモノの関わりを整理して、必要になりそうなオブジェクトを探していく。

重要そうな箇所から、独立した部分として作り始める

キャンセルよりは入金を、入金よりは受注を……と、より業務の根幹に近い(と思われる)部分から作り始めてみる。

さらに、「受注」機能は「販売可能数量」クラスを参照する、「販売可能数量」は計算に「在庫数」クラスを使う、「在庫数」クラスは「在庫単位(個なのか箱なのか…)」クラスを持つ、というように「数量」に関わる部分だけ切り出すことが出来そう、となれば、「受注」機能とは独立した「数量」パッケージを作る。

そして、例えば『「在庫は足りるー?」と聞かれたら「足りてるよ~」と答える』だけの処理に注目してクラスを作ることで、全体から切り離してテストすることができるし、変更も用意になる。

手続き型とオブジェクト指向の違い

手続き型で設計した場合

public Price calculatePrice(Age customerAge, Type customerType){

    Price price;
    if(customerAge < 6){
        price = 0;
    }else if(customerAge < 12){
        price = 50;
    }else if(customerAge < 65){
        if(customerType == "nomal"){
            price = 100;
        }else if(customerType == "silver"){
            price = 75;
        }else if(customerType == "gold"){
            price = 50;
        }
    }else{
        price = 75;
    }
    return price;

}

ifがいっぱい。

オブジェクト指向で設計した場合

// CalcuraltePrice.java
public Price CalculatePrice(Customer customer){
    // Customerクラス内に、年齢・会員種別に応じた料金を返すメソッドを作っておく
    Price price = customer.getPrice();
    return price;
}

ifがいらない(Customerクラス自身が判断をする)。

小規模なアプリなら、手続き型で書いて、ちょっとした変更ならifを一つ増やすだけで良いけど、大規模なアプリになってくると、同じようなif文があっちにもこっちにも出きてきて、ifはどんどん入れ子になって、最終的に「ぴえん」ってことになって行くので、ある程度以上に拡張が見込まれるアプリなら断然オブジェクト指向的に書いた方が、安全。

ドメインクラスは改善を繰り返して成長させる

最初は会員番号と氏名だけだったCustomerクラスに、住所が増え、電話番号が増え、メールアドレスが増え……としていく。それは正しい。

でも、何も考えずにフィールド変数を増やしていくと、ドメインクラスが肥大化していって、結局変更に弱いアプリケーションになってしまう。

例えば、じゃあ郵便番号と住所はまとめて「住所」クラスとして分離するとか、電話番号とメールアドレスとSNSアカウント情報をまとめて「連絡先」クラスに分離する、など、常に一つのクラスが小さくなるように改善を続けていくことで、より洗練されたドメインオブジェクトが出来ていく。

つづきはこちら。


この記事が気に入ったらサポートをしてみませんか?