AngularJS、C#、ASP.Net Web API、およびFluent NHibernateを使用してWebアプリを構築しています。 DTOを使用してプレゼンテーション層にデータを転送することにしました(angular views)。DTOの一般的な構造化と命名に関していくつかの疑問がありました。ここに私のシナリオを説明する例を示します。次のようなCustomerというドメインエンティティがあるとします。
public class Customer
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual Address Address { get; set; }
public virtual ICollection<Account> Accounts { get; set; }
}
次に、ビュー/プレゼンテーションレイヤーで、次のようなさまざまな種類のCustomerを取得する必要があります。
1)Id、Name、2)Id、Name、Address 3)Id、Name、Address、Accounts
これを実現するために一連のDTOを作成しました。
public class CustomerEntry
{
public int Id { get; set; }
public string Name { get; set; }
}
public class CustomerWithAddress : CustomerEntry
{
public AddressDetails Address { get; set; }
}
public class CustomerWithAddressAndAccounts : CustomerWithAddress
{
public ICollection<AccountDetails> Accounts { get; set; }
}
AddressDetailsとAccountDetailsは、対応するドメインエンティティのすべてのプロパティを持つDTOです。
これは、クエリおよびデータ取得に適しています。問題は、挿入と更新に何を使用するかです。新しい顧客レコードの作成中、名前と住所は必須であり、アカウントはオプションです。つまり、すべての顧客プロパティを持つオブジェクトが必要です。したがって、混乱:
1)挿入と更新には何を使用しますか? CustomerWithAddressAndAccounts DTOにはすべてが含まれていますが、その名前を挿入/更新に使用するのは少し厄介なようです。
2)別のDTOを作成しますか?作成した場合、新しいDTOはCustomerWithAddressAndAccountsとまったく同じになるため、それは重複しませんか?
3)最後になりましたが、上記のDTO継承構造は要件に適しているように見えますか?これをモデル化する他の方法はありますか?
私はこのトピックに関する他の記事を読みましたが、あまり前進できませんでした。私がピックアップしたことの1つは、クラス名にサフィックス「DTO」を使用しないことです。少し不必要だと思う。
あなたの考えを聞いてみたい
ありがとう
推奨事項は、各エンティティにDTOを付加したDTOクラスを1つだけ持つことです。 CustomerEntryDTO
のCustomer
entity
(ただし、選択と要件に応じて、継承階層を使用できます)。
さらに、抽象クラスDTOBase
の基本クラスまたはインターフェイスを追加します。また、子DTOに含まれる各Address、Account、およびその他のプロパティに対して、このような深い継承階層を使用しないでください。むしろ、これらのプロパティを以下と同じCustomerEntryDTO
クラス(可能な場合)に含めます。
[Serializable]
public class CustomerEntryDTO : DTOBase, IAddressDetails, IAccountDetails
{
public int Id { get; set; }
public string Name { get; set; }
public AddressDetails Address { get; set; } //Can remain null for some Customers
public ICollection<AccountDetails> Accounts { get; set; } //Can remain null for some Customemer
}
さらに、DTOは、プロセスの境界を越えて渡されるようにシリアル化できる必要があります。
DTOパターンのmoreについては、以下の記事を参照してください。
編集:特定のプロパティをネットワーク経由で送信したくない場合これ)、 NonSerialized
などの属性を使用して、シリアル化メカニズムからそれらを除外できます(ただし、プロパティではなくフィールドでのみ動作します。プロパティで使用するための回避策の記事を参照してください: NonSerialized on property )。また、ExcludeFromSerializationAttribute
などの独自のカスタム属性を作成し、特定のルール/条件に基づいて毎回送信する必要のないプロパティに適用することもできます。 以下も参照してください:条件付きXMLシリアル化
編集2:インターフェイスを使用して、1つのCustomerEntryDTO
クラスの異なるプロパティを分離します。 GoogleまたはMSDNのInterface Segregation Principleをご覧ください。後でサンプルの説明を書きます。
挿入と更新には何を使用しますか?
サービスオペレーションは通常、ビジネスオペレーションと非常に密接な関係で定義されます。ビジネス言語は「挿入」や「更新」の観点では話せませんし、サービスもそうではありません。
顧客管理サービスには、顧客名と他のオプションパラメータを受け取るRegister
操作が含まれる可能性があります。
別のDTOを作成しますか?
はい、別のDTOを作成する必要があります。
サービス操作の契約で十分な場合があり、特定の操作に対して個別のDTOを定義する必要はありません。
function Register(UserName as String, Address as Maybe(of String)) as Response
ただし、ほとんどの場合、単一のサービス操作に対しても別個のDTOクラスを定義する方が適切です。
class RegisterCommand
public UserName as String
public Address as Maybe(of String)
end class
function Register(Command as RegisterCommand) as Response
RegisterCommand
DTOはCustomerWithAddress
DTOと非常によく似ていますが、これは同じフィールドを持っていますが、実際にはこれら2つのDTOは非常に異なる意味を持ち、互いを置き換えません。
たとえば、CustomerWithAddress
にはAddressDetails
が含まれますが、単純なString
アドレス表現で顧客を登録するには十分かもしれません。
サービス操作ごとに個別のDTOを使用すると、作成に時間がかかりますが、保守が容易になります。