6

Addressを値オブジェクトとしてモデル化したいと思います。それは不変にするのがよい習慣であるので、私は後でそれを変更することができるかもしれないセッターを提供しないことを選択しました。DDD:複雑な値オブジェクトを不変に保つ方法は?

一般的なアプローチは、データをコンストラクタに渡すことです。値オブジェクトはかなり大きい場合しかし、それは非常に肥大化になることがあります。

class Address { 
    public function __construct(
     Point $location, 
     $houseNumber, 
     $streetName, 
     $postcode, 
     $poBox, 
     $city, 
     $region, 
     $country) { 
     // ... 
    } 
} 

別のアプローチは、クリーンなコンストラクタで、その結果、配列として引数を提供することが、whouldことかもしれないが台無しの実装コンストラクタ:

class Address { 
    public function __construct(array $parts) { 
     if (! isset($parts['location']) || ! $location instanceof Point) { 
      throw new Exception('The location is required'); 
     } 
     $this->location = $location; 
     // ... 
     if (isset($parts['poBox'])) { 
      $this->poBox = $parts['poBox']; 
     } 
     // ... 
    } 
} 

これも私にとっては不自然なようです。

かなり大きな値のオブジェクトを正しく実装する方法についてのアドバイスはありますか?

+1

個人的には、値オブジェクトが問題を引き起こすほど大きく、複数の値オブジェクトに分割する必要があると思います。アドレスの例は私の個人的な感性には問題ないようですが、大きすぎると場所+番地+都市(都市には地域と国が含まれます)になる可能性があります。 – Domenic

+0

@Domenic:これは面白いアプローチです! – Benjamin

答えて

12

large list of parametersの主な問題は、パラメータを混在させる可読性と危険です。 Effective Javaに記載されているように、Builder patternでこれらの問題に取り組むことができます。これは、(名前が付けられ、オプションのパラメータをサポートしていない、特に言語)コードをより読みやすくなります:

public class AddressBuilder { 
    private Point _point; 
    private String _houseNumber; 

    // other parameters 

    public AddressBuilder() { 
    } 

    public AddressBuilder WithPoint(Point point) { 
     _point = point; 
     return this; 
    } 

    public AddressBuilder WithHouseNumber(String houseNumber) { 
     _houseNumber = houseNumber; 
     return this; 
    } 

    public Address Build() { 
     return new Address(_point, _houseNumber, ...); 
    } 
} 

Address address = new AddressBuilder() 
    .WithHouseNumber("123") 
    .WithPoint(point) 
    .Build(); 

利点を:それはミックスしにくい

  • より読みやすいよう

    • パラメータの名前は地域番号付き
    • は、独自のパラメータの順序を使用できます。
    • 省略可能なパラメータは省略できます

    私が考えることができる1つの短所は、コンストラクタを使用するときにコンパイル時のエラーではなく、実行時エラーが発生する引数の1つ(たとえばWithHouseNumberを呼び出さない)を指定するのを忘れることです。たとえば、(文字列を渡すことに反対する)PostalCodeのようなより多くのバリューオブジェクトを使用することも検討する必要があります。

    関連するメモでは、ビジネス要件によっては、Value Objectの一部が変更されることがあります。たとえば、住所が最初に入力されたときに、番地のスペルが間違っている可能性があり、修正する必要があります。あなたが不変オブジェクトとしてAddressをモデル化して以来、セッターはありません。この問題の1つの可能な解決策は、アドレス値オブジェクトに「副作用のない関数」を導入することです。この関数は新しい通りの名前を除いて、オブジェクト自体のコピーを返します:

    public class Address { 
        private readonly String _streetName; 
        private readonly String _houseNumber; 
    
        ... 
    
        public Address WithNewStreetName(String newStreetName) { 
         // enforce street name rules (not null, format etc) 
    
         return new Address(
          newStreetName 
          // copy other members from this instance 
          _houseNumber); 
        } 
    
        ... 
    } 
    
  • +0

    非常に興味深い答えは、ありがとう。それはファクトリーとも呼ばれているのですか、またはエンティティに予約されているファクトリパターンですか? – Benjamin

    +0

    DDD Factoryを使用して、複雑なバリューオブジェクトを構築することができます。このBuilderはDDD Factoryと見なすことができます。 – Dmitry

    +0

    この回答は正しいです! – jpierson

    -1

    不変不変のは、高いパフォーマンスと優れた拡張性のためである同時計算、ブロッキングとロックなし、に適合しています。

    バリューオブジェクトは、コンカレントシステムでよりよく動作し、システムを配布し、古いVOを新しいVOに置き換えることができます。

    0

    これはドメインドリブンデザインの例でよく見られる問題です。ドメインエキスパートが不足しているのは、Addressとその要件について教えてくれる人物です。私は、ドメインエキスパートがあなたにアドレスがポイントを持たないことを教えてくれると思うでしょう。アドレスからポイントを生成することは可能かもしれませんが、ポイントは必要ありません。また、P.O. Boxは住所で別個の値ではありません。郵便局ボックスアドレスクラス(POBoxAddress)が必要な場合があります。これは、このクラスが配送業者または請求先ドメインエキスパートではない開発者によって定義されたように見えるためです。ドメインエキスパートと話すことで、コンストラクタのパラメータ数を減らすことができます。

    第2
    パラメータを値オブジェクトとしてグループ化することができます。 City値オブジェクトを作成することができます。それには、市、州/州、国が必要です。私は、地域と国を知らない限り、都市名はあまり意味がないと思うでしょう。 パリとは、パリ、イリノイ、アメリカ、パリのいずれかを意味し、イルドフランス、FRはあなたに完全な画像を与えます。したがって、これはCountオブジェクトのCountをAddressオブジェクトに減らします。

    あなたがコーディングしているドメインのドメインエキスパートを探すDDD道路に行った場合、あなたは専門家ではありません。場合によっては、コードやきれいなデザインパターンで問題を修正してはいけません。

    関連する問題