2009-05-22 9 views
19

私はdifferences between POCO and DTOを見ていました(POCOは動作(メソッド?)であると思われます)、貧血ドメインモデルのMartin Fowlerがthis articleを見つけました。POCO's、DTO's、DLL's、Anemic Domain Models

理解の不足により、これらの貧血ドメインモデルの1つを作成したと思います。

私のアプリケーションの1つに、私はビジネスドメインエンティティを 'dto' dllで定義しています。彼らはゲッターとセッターの多くのプロパティを持っています。私のビジネスロジックコード(populate、calculate)は別の 'bll' dllにあり、データアクセスコードは 'dal' dllにあります。 「ベストプラクティス」と思った。

だから典型的私はそうのようなDTOを作成する:

dto.BusinessObject bo = new dto.BusinessObject(...) 

などのようなBLL層に渡す。次に、いくつかのロジックを実行し、DALに渡し

bll.BusinessObject.Populate(bo); 

層のように:

dal.BusinessObject.Populate(bo); 

私の理解から、自分のdOCをポコにするためには、私はビジネスログを作る必要がありますcとオブジェクトの動作(メソッド)の一部です。だから、上のコードの代わりに、それはもっと似ています:

poco.BusinessObject bo = new poco.BusinessObject(...) 
bo.Populate(); 

ieです。メソッドにオブジェクトを渡すのではなく、オブジェクトのメソッドを呼び出しています。

私の質問は - 私はこれを行うことができますし、懸念(別のdllのなど...)の 'ベストプラクティス'のレイヤーを保持します。オブジェクト上のメソッドを呼び出さないということは、そのメソッドがオブジェクト内に定義されていなければならないということですか?

私の混乱を助けてください。

答えて

22

通常、ドメインオブジェクトはそのビジネスモデルの一部ではないため、永続性を導入したくはありません(飛行機自体は構成されず、乗客/貨物をある場所から別の場所に飛ばす)。 repository patternORM framework、またはその他のデータアクセスパターンを使用して、オブジェクトの状態の永続的なストレージおよび取得を管理する必要があります。あなたはこのようなことをやっている時に貧血のドメインモデルを再生するには来て

は次のようになります。この場合、

IAirplaneService service = ...; 
Airplane plane = ...; 
service.FlyAirplaneToAirport(plane, "IAD"); 

、航空機の状態の管理を(それはATのどこに何があるか、飛んでいるかどうか到着時間/空港は何か、飛行計画は何かなど)は、飛行機外の何かに委譲されます...飛行機サービスインスタンス。

あなたのインターフェースをこのように設計することであろう、これを実現するための

A POCO方法:

Airplane plane = ...; 
plane.FlyToAirport("IAD"); 

開発者は飛行機がちょうどそれを行うには飛行機を伝える(飛ぶように見るために場所を知っているので、これは、より多くの発見であります)。また、状態がで、内部で管理されるのはであることを保証することもできます。現在の場所のようなものを読み取り専用にして、その場所が1か所で変更されていることを確認できます。貧血ドメインオブジェクトでは、状態が外部に設定されているため、ドメインの規模が拡大するにつれて状態が変更される場所を見つけることがますます困難になります。

+4

このようなデザインのソースコードの例を知っていますか?私は原則が理にかなっていますが、私がFlyToAirportの実装に着手すると、特に私がSPを使用していない場合、いくつかのテーブルを更新する必要がある場合は、データベースへのチャット・インターフェイスで終わります。 –

+1

外部トランザクションが関係する場合、サービスを使用する方が意味があります。できるだけ粗くして、ドメインモデルに多くの動作を組み込むことができるようにする必要があります。残念ながら、私はそれができるだけ頻繁に使用されて表示されません。確かに、私が書いた非旧生産コードではこのコードを使用し、このアプローチを使用するいくつかのプロジェクトに取り組んできました。 –

6

個人的には私はそれらの貧血ドメインモデルがあまりにも悪いとは思っていません。私は本当に、動作だけでなく、データだけを表現するドメインオブジェクトを持つというアイデアが気に入っています。私はこのアプローチの主な欠点はコードの発見可能性だと思う。それらを使用するために利用可能なアクションを知る必要があります。その周りを取得し、まだモデルから切り離さ行動コードを維持するための一つの方法は、行動のためのインターフェースを導入することである。

interface ISomeDomainObjectBehaviour 
{ 
    SomeDomainObject Get(int Id); 
    void Save(SomeDomainObject data); 
    void Delete(int Id); 
} 

class SomeDomainObjectSqlBehaviour : ISomeDomainObjectBehaviour 
{ 
    SomeDomainObject ISomeDomainObjectBehaviour.Get(int Id) 
    { 
     // code to get object from database 
    } 

    void ISomeDomainObjectBehaviour.Save(SomeDomainObject data) 
    { 
     // code to store object in database 
    } 

    void ISomeDomainObjectBehaviour.Delete(int Id) 
    { 
     // code to remove object from database 
    } 
} 
class SomeDomainObject 
{ 
    private ISomeDomainObjectBehaviour _behaviour = null; 
    public SomeDomainObject(ISomeDomainObjectBehaviour behaviour) 
    { 

    } 

    public int Id { get; set; } 
    public string Name { get; set; } 
    public int Size { get; set; } 


    public void Save() 
    { 
     if (_behaviour != null) 
     { 
      _behaviour.Save(this); 
     } 
    } 

    // add methods for getting, deleting, ... 

} 

あなたはモデルから分離振る舞いの実装を保つことができますこの方法。モデルに注入されたインタフェース実装を使用することで、簡単に動作を模擬することができるため、コードをテストするのが簡単になります。

+2

行動の管理方法は、戦略パターン(http://en.wikipedia.org/wiki/Strategy_pattern)に似ています。実際の動作を実行時に決定する必要があるかもしれないが、そうでなければ使用するときには過剰設計につながる可能性があるときは、本当に良いです。私はこのパターンが好きです。なぜなら、クラス階層の外側で再利用できるようにするからです。しかし、解決策が複雑すぎるのを避けるために必要でない限り、意識的に使用しないようにする必要があります。 –

+0

はい、動作を別のクラスに委任するのと同じですが、呼び出し元のコードは依然としてドメインオブジェクトを通じて動作にアクセスしています。 (おそらく、これを保証するために動作クラスを内部にするかもしれません) – Rodi

10

私はこれを明確にするための最良の方法として定義されていると思う:

DTO:データ転送オブジェクト:

を彼らは通常、プレゼンテーション層とサービス層の間でデータの輸送のために役立ちます。それ以上のものはありません。一般的に、getsとsetを持つクラスとして実装されています。

public class ClientDTO 
{ 
    public long Id {get;set;} 
    public string Name {get;set;} 
} 

BO:ビジネスオブジェクト:

ビジネスオブジェクトビジネスの要素を表しており、自然のベストプラクティスは、彼らはまた、ビジネスロジックを含まなければならないと言います。 Michael Meadowsによると、このオブジェクトからのデータアクセスを分離することも良い方法です。

public class Client 
{ 
    private long _id; 
    public long Id 
    { 
     get { return _id; } 
     protected set { _id = value; } 
    } 
    protected Client() { } 
    public Client(string name) 
    { 
     this.Name = name;  
    } 
    private string _name; 
    public string Name 
    { 
     get { return _name; } 
     set 
     { // Notice that there is business logic inside (name existence checking) 
      // Persistence is isolated through the IClientDAO interface and a factory 
      IClientDAO clientDAO = DAOFactory.Instance.Get<IClientDAO>(); 
      if (clientDAO.ExistsClientByName(value)) 
      { 
       throw new ApplicationException("Another client with same name exists."); 
      } 
      _name = value; 
     } 
    } 
    public void CheckIfCanBeRemoved() 
    { 
     // Check if there are sales associated to client 
     if (DAOFactory.Instance.GetDAO<ISaleDAO>().ExistsSalesFor(this)) 
     { 
      string msg = "Client can not be removed, there are sales associated to him/her."; 
      throw new ApplicationException(msg); 
     } 
    } 
} 

サービスまたはアプリケーションのクラス これらのクラスは、ユーザーとシステムの間の相互作用を表し、彼らはClientDTOとクライアントの両方を利用します。

public class ClientRegistration 
{ 
    public void Insert(ClientDTO dto) 
    { 
     Client client = new Client(dto.Id,dto.Name); /// <-- Business logic inside the constructor 
     DAOFactory.Instance.Save(client);   
    } 
    public void Modify(ClientDTO dto) 
    { 
     Client client = DAOFactory.Instance.Get<Client>(dto.Id); 
     client.Name = dto.Name; // <--- Business logic inside the Name property 
     DAOFactory.Instance.Save(client); 
    } 
    public void Remove(ClientDTO dto) 
    { 
     Client client = DAOFactory.Instance.Get<Client>(dto.Id); 
     client.CheckIfCanBeRemoved() // <--- Business logic here 
     DAOFactory.Instance.Remove(client); 
    } 
    public ClientDTO Retrieve(string name) 
    { 
     Client client = DAOFactory.Instance.Get<IClientDAO>().FindByName(name); 
     if (client == null) { throw new ApplicationException("Client not found."); } 
     ClientDTO dto = new ClientDTO() 
     { 
      Id = client.Id, 
      Name = client.Name 
     } 
    } 
} 
+0

+1良い見通し – boj