私は現在、現在のプロジェクトでソロ開発者として働いています。私は別の開発者からプロジェクトを引き継ぎました。これは、C#のモデルビューコントローラースタイルのWebアプリケーションです。オブジェクトリレーショナルマッピングにEntity Frameworkを使用します。また、ドメインモデルの型には、2つの異なるクラスセットがあります。 1つのセットはORMとの対話に使用され、もう1つのセットはMVCシステムのモデルとして使用されます。たとえば、次の2つのクラスがあるとします。
public class Order{
int ID{get;set;}
String Customer{get;set;}
DateTime DeliveryDate{get;set;}
String Description{get;set;}
}
そして
public class OrderModel{
String Customer{get;set;}
DateTime DeliveryDate{get;set;}
String Description{get;set;}
public OrderModel( Order from){
this.Customer= from.Customer;
// copy all the properties over individually
}
public Order ToOrder(){
Order result =new Order();
result.Customer = this.Customer;
// copy all the properties over individually
}
}
このアプローチにはいくつかの欠点があると思います(何かが変更された場合にコードを変更する場所が増える、メモリ内にオブジェクトが増える、データのコピーに費やす時間が増える)が、ここでの利点はよくわかりません。モデルクラスの柔軟性が増したと思いますか?しかし、エンティティクラスもサブクラス化することで取得できます。私の傾向は、これらの2つのクラスグループをマージするか、モデルクラスをエンティティクラスのサブクラスにすることです。ここで重要なものが欠けていますか?これは私が知らない一般的なデザインパターンですか?私が考えているリファクタリングを行わない理由はありますか?
[〜#〜]更新[〜#〜]
ここでの回答のいくつかは、プロジェクトの最初の説明に重要な詳細が欠けていたことに気づかせています。プロジェクトには、ページモデルクラスという3つ目のクラスグループもあります。それらは、ページをバッキングするモデルとして実際に使用されているものです。また、UIに固有の情報も含まれており、注文とともにデータベースに保存されることはありません。ページモデルクラスの例は次のようになります。
public class EditOrderPagelModel
{
public OrderModel Order{get;set;}
public DateTime EarliestDeliveryDate{get;set;}
public DateTime LatestAllowedDeliveryDate{get;set;}
}
この3番目のグループの有用性はここではまったく異なると思いますが、他のものとマージする予定はありません(名前を変更する場合もあります)。
モデルグループのクラスは現在、アプリケーションのAPIでも使用されています。これは、それが良いアイデアであるかどうかについての意見を聞くことにも興味があります。
また、ここで文字列として表されている顧客は例を単純化するためのものであり、実際にはシステムでそのように表されているためではありません。実際のシステムでは、独自のプロパティを持つドメインモデルの異なるタイプとして顧客がいます
ここで重要な何かが欠けていますか?
[〜#〜]はい[〜#〜]
これらは同じように見え、ドメイン内で同じものを表しますが、同じ(OOP)オブジェクトではありません。
1つは、コードのデータストレージ部分で知られているOrder
です。もう1つは、UIで認識されているOrder
です。これらのクラスが同じプロパティを持つことは楽しい偶然の一致ですが、それらは保証されていません。
または、別の視点からそれを見るには、単一責任原則を検討してください。ここでの重要な動機は、「クラスには変更する理由が1つある」ということです。 特に ORMやUIフレームワークなどの普及しているフレームワークを扱う場合、オブジェクトは、多くの場合、フレームワークによってエンティティが変更されます。
エンティティをbothの両方のフレームワークに結び付けることで、それをさらに悪化させています。
ビューモデルの目的は、ビューが必要とする形状でデータを提供することにより(モデルに依存せず)、(特にMVVMでは)ビューロジックの一部またはすべてをビューから押し戻すことにより、2つの方法で分離を提供することです。ビューモデルに。
完全に正規化されたモデルでは、Customerはモデルでは文字列ではなくIDで表されます。モデルは、顧客の名前が必要な場合、OrdersテーブルにあるCustomerIDを使用してCustomersテーブルから名前を検索します。
一方、ビューモデルは、IDではなく顧客のName
に関心がある可能性があります。お客様がモデルで更新されていない限り、IDは必要ありません。
さらに、ビューモデルは異なる形状を取ることができます(通常、それは純粋です [〜#〜] crud [〜#〜] でない限り)。請求書ビューモデルオブジェクトには顧客名、住所、およびラインアイテムが含まれる場合がありますが、モデルには顧客と説明が含まれています。
言い換えると、請求書は通常、表示される方法と同じ方法では保存されません。
いくつかの点であなたは正しいです:それらは両方ともrefer同じドメインオブジェクト(order
)を参照します。しかし、あなたはこれまでのところ間違っています。それは、異なる目的から生まれた、別の表現よりも実際の複製ではありません。注文を継続したい場合は、すべての列にアクセスしたい。しかし、それを外部に漏らしたくないのです。全体Entitiesをワイヤ経由でプッシュする以外に、本当に望んでいることではありません。いくつかのkbで十分なorders
のMBのコレクションがクライアントに出荷されたケースを知っています。
長い話を簡単に言うと、これを行う主な理由は次のとおりです。
1)カプセル化-アプリケーションの情報隠蔽
2)小規模モデルはシリアル化が高速で送信が簡単
)は重複していますが、目的が異なります。したがって、異なるオブジェクトが存在する必要があります。
4)クエリごとに異なるValue-Objects。 C#ではどうなるかわかりませんが、Javaでは[〜#〜] pojo [〜#〜]結果を収集します。単一のorder
が必要な場合は、Order-Entityを使用しますが、 smallerオブジェクトを使用して、データの量をいくつかの列で埋めるだけで十分です。
(免責事項:この方法で使用されているのを見ただけです。実際の使用目的を誤解している可能性があります。この回答は疑いを持って扱ってください。)
もう1つの不足しているビットは、Order
とOrderModel
間の変換です。
Order
クラスはORMに関連付けられており、OrderModel
はビューモデルの設計に関連付けられています。
通常、この2つの方法は「魔法の方法」で提供されます(DI、IoC、MVVMなどと呼ばれることもあります)。つまり、これら2つの変換メソッドをOrderModel
の一部として実装する代わりに、これらの変換メソッドは、これらの相互変換専用のクラスに属します。そのクラスはフレームワークになんらかの方法で登録され、フレームワークが簡単に検索できるようになります。
public class OrderModel {
...
public OrderModel(Order from) { ... }
public Order ToOrder() { ... }
}
これを行う理由は、OrderModel
からOrder
へ、またはその逆の交差があるときは常に、一部のデータがORMからロードまたはORMに保存されている必要があることを知っているためです。