Domain Primitive usage recommendation

1. はじめに

最近、チーム内のメンバーのコードレビューを行ったところ、多くの問題が発見されました。

その一つが、全体的にコードの凝集性が不十分であり、本来オブジェクトが提供すべきメソッドが外部に漏れ出していることです。

たとえば、オブジェクトにステータスフィールドが含まれている場合、ユーザー側でステータスに基づいて成功かどうかを判断する必要があります。

public class SomeResult{

//0 の値は成功を示す

private String status;

//ラベル。HOT は人気を意味する

private String tage;

private Map extInfo;

//その他は省略

}

ユーザー側のコード:

if("0".equals(result.getStatus()) && "HOT".equals(result.getTag())){

//ロジックを実行する

}

Map extInfo = result.getExtraInfo();

extInfo.put("xxx", YYY);

本来、ユーザーが行うべきことは、結果オブジェクトに「成功したかどうか」や「人気素材かどうか」を判断させる(=オブジェクト自身に語らせる)だけです。

しかし、Result オブジェクトがプロパティと Getter / Setter メソッドしか持たないため、この部分のロジックを外部から感知する必要があります。

これは本質的に、貧血症モデルのコードを書く習慣によるものです。すなわち、オブジェクトには属性と代入・取得メソッドのみを定義し、ビジネスロジックをサービス層に任せて処理しています。

これはステップ指向のプログラミングに近いものの、デザインパターンであるデメテルの法則に違反しています。

デメテルの法則は最小知識の原則とも呼ばれます。クラスが他のクラスについて知るべきことは少ないほど良い、すなわちオブジェクトは他のオブジェクトについてできるだけ知るべきではなく、親しいオブジェクトとのみ通信し、見知らぬオブジェクトとは通信すべきでないという原則です。英語略称は LOD です。

また、「複雑性をカプセル化する」というクラス設計原則にも違反しています。




2 既存の問題




2.1 ソフトウェア設計原則への違反

これは、ソフトウェア工学の分野における「高い凝集性と低い結合性」という設計原則に違反しています。

2.2 不要なマジックバリュー

同様のシナリオでは、具体的なステータスコードやエラーコードを外部から感知する必要があり、様々なステータスコードで判定してから対応するロジックを実行する必要があります。

インターフェイス提供者が列挙型を提供した場合でも、呼び出し元は不要なロジックまで感知する必要があります。

インターフェイス提供者が列挙型を提供しない場合、ユーザーは自分で定数や列挙型を定義する必要があります。

しかし、多くの開発者は手を抜く傾向があり、マジックバリューで直接判定してしまうことが多く、その結果コードの可読性が低下します。

コードの読み手は、それらの数字や文字列が何を表しているのか全く分かりません。




3 ソリューション

3.1 充血モデル

オブジェクトにはデータだけでなく、そのデータに対する操作も含めるべきです。

より包括的な内容を理解するには、DDD の関連知識を体系的に学ぶ必要があります。

ただし、多くのチームのプロジェクトがドメイン駆動設計の思想とアーキテクチャで開発されていないのが実情です。

しかし、だからといって、ドメイン駆動設計の概念を参考にクラスを設計できないわけではありません。

Domain Primitive について学び、完全に貧血症なクラスを Domain Primitive として、さらには DDD のエンティティや集約ルートとして設計することができます。

3.2 Domain Primitive

3.2 この一部は、Alibaba の技術専門家が解説する DDD シリーズ「Domain Primitive」からの転載です。

3.2.1 Domain Primitive の定義

Domain Primitive (DP) は、特定分野において正確な定義、自己検証、および動作を持つ値オブジェクトです。

DP は伝統的な意味での値オブジェクトであり、不変性の特徴を持ちます。

DP は正確な定義を持つ完全な概念です。

DP はビジネスドメインのネイティブ言語を使用します。

DP はビジネスドメインの最小コンポーネントとなり得ると同時に、複雑な組み合わせを構築することもできます。

注: Domain Primitive の概念と名称は、Dan Bergh Johnsson と Daniel Deogun による著書「Secure by Design」に由来します。

3.2.2 Domain Primitive を使用する際の 3 つの原則

• 暗黙的な概念を明示的にする

• 暗黙的なコンテキストを明示的にする

• 複数オブジェクトの動作をカプセル化する

3.2.3 Domain Primitive と DDD における値オブジェクトの違い

DDD には、値オブジェクトの概念が既に存在します:

Evans の DDD ブルーブックでは、値オブジェクトは主に非エンティティの値オブジェクトとして扱われています。

Vernon の IDDD レッドブックでは、著者は値オブジェクトの不変性、Equals メソッド、ファクトリメソッドなどにより注目しています。

Domain Primitive は値オブジェクトの進化版です。従来の VO を基盤として、各 DP は単なる値オブジェクトではなく、概念として完全であることが求められます。不変の VO を基盤に有効性と動作が追加されています。ただし、同様に副作用がないという要件があります。

3.2.4 Domain Primitive とデータ転送オブジェクト (DTO) の違い

ここに画像の説明を挿入

3.2.5 Domain Primitive の使用場面

DP の一般的な使用場面には以下が含まれます:

• フォーマット制限付きの文字列: 名前、電話番号、注文番号、郵便番号、住所など

• 制限付きの整数: OrderId (>0)、パーセンテージ (0-100%)、数量 (>=0) など

• 列挙可能な整数: ステータス (デシリアライゼーションの問題から、一般的に列挙型は使用しない)

• Double または BigDecimal: 一般的に使用される Double や BigDecimal でビジネス上の意味を持つもの。温度、金額、合計、為替レート、評価など

• 複雑なデータ構造: たとえば Map など、マップのすべての操作をラップし、必要な動作のみを公開するよう努める


ビジネス指向のプログラミングです。




3.3 簡略化ケース

最初のケースは、複雑性をオブジェクト内にカプセル化して上流から利用しやすくするよう最適化できます。

もちろん、この例は非常にシンプルです。概念を理解してもらうためのものに過ぎず、実際の開発で遭遇するシナリオははるかに複雑で、カプセル化すべきメソッドも多数あるでしょう。

実際の運用では、以下を行うことができます:

パラメータの妥当性検証

ビジネスステータスの判定

このタイプの属性に関連する処理メソッド

オブジェクトの内部プロパティを取得した後に実行する必要がある操作

...

これらをすべてオブジェクトにカプセル化し、結合度を下げ、複雑性をカプセル化します。




4 まとめ

ほとんどのプログラマーは貧血症モデルのコードを書くことに慣れており、ドメイン駆動設計の概念と実践を学ぶことに消極的です。

多くのプログラマーは、ドメイン駆動設計の思想とフレームワークを明確に使用していない限り、チームプロジェクトでは充血モデルのコードを書くべきではない、あるいは書く必要がないと無意識に信じています。

世界は白か黒かではありません。チームが明示的にドメイン駆動設計を使用していない場合でも、たとえ貧血症モデルが蔓延している場合でも、設計原則をコーディングに浸透させることができます。


Related Articles

Explore More Special Offers

  1. Short Message Service(SMS) & Mail Service

    50,000 email package starts as low as USD 1.99, 120 short messages start at only USD 1.00

phone お問い合わせ
Hi, I'm Alibaba Cloud AI Assistant!
I can help with questions and solutions.