データアクセスを分離する理由
この本から、「モデル駆動型設計」の章の最初の2ページは、ドメインモデルの実装から技術的な実装の詳細を抽象化したい理由を正当化するものだと思います。
- ドメインモデルとコードの間の密接な接続を維持したい
- 技術的な懸念を分離することで、モデルが実装に実用的であることを証明できます
- ユビキタス言語をシステムの設計に浸透させたい
これは、システムの実際の実装から切り離される別の「分析モデル」を回避するためのすべてのようです。
この本を理解したところ、この「分析モデル」はソフトウェアの実装を考慮せずに設計されてしまう可能性があると書かれています。開発者は、ビジネス側が理解したモデルを実装しようとすると、必要に応じて独自の抽象化を形成し、コミュニケーションと理解の壁を引き起こします。
反対に、ドメインモデルにあまりにも多くの技術的な懸念を持ち込む開発者は、この分裂を引き起こす可能性もあります。
したがって、持続性などの懸念の分離を実践することは、これらの設計に対する分析モデルの発散を防ぐのに役立つと考えることができます。持続性などをモデルに導入する必要があると感じた場合、それは危険信号です。多分このモデルは実装には実用的ではありません。
引用:
「デザインは、慎重に検討されたモデルの直接の結果となったため、単一モデルはエラーの可能性を低減します。デザイン、さらにはコード自体も、モデルの伝達性を持っています。」
私がこれを解釈している方法では、データベースアクセスのようなものを処理するコードの行が多くなった場合、その伝達性が失われます。
データベースにアクセスする必要性が一意性のチェックなどの目的である場合は、以下を参照してください。
Udi Dahan:チームがDDDを適用するときに犯す最大の間違い
http://gojko.net/2010/06/11/udi-dahan-the-biggest-mistakes-teams-make-when-applying-ddd/
「すべてのルールが等しく作成されていない」の下
そして
ドメインモデルパターンの採用
http://msdn.microsoft.com/en-us/magazine/ee236415.aspx#id0400119
「ドメインモデルを使用しないシナリオ」では、同じ主題に触れています。
データアクセスを分離する方法
インターフェースを介したデータの読み込み
「データアクセスレイヤー」は、必要なデータを取得するために呼び出すインターフェイスを通じて抽象化されています。
var orderLines = OrderRepository.GetOrderLines(orderId);
foreach (var line in orderLines)
{
total += line.Price;
}
長所:このインターフェースでは、「データアクセス」配管コードが分離されているため、テストを記述できます。データアクセスはケースバイケースで処理できるため、一般的な戦略よりも優れたパフォーマンスが得られます。
短所:呼び出しコードは、何がロードされ、何がロードされていないかを想定する必要があります。
たとえば、GetOrderLinesは、パフォーマンス上の理由から、ProductInfoプロパティがnullのOrderLineオブジェクトを返します。開発者は、インターフェイスの背後にあるコードについて詳しく知っている必要があります。
私は実際のシステムでこの方法を試しました。パフォーマンスの問題を修正するために、常に読み込まれる対象の範囲を変更することになります。ロードされているものとロードされていないものを確認するためにデータアクセスコードを確認するために、インターフェイスの後ろをのぞき込むことになります。
現在、懸念事項を分離することで、開発者はコードの1つの側面にできるだけ集中することができます。インターフェース手法は、このデータがロードされた方法を削除しますが、ロードされたHOW MUCHデータはロードされません。
結論:かなり低い分離!
遅延読み込み
データはオンデマンドで読み込まれます。データをロードするための呼び出しは、オブジェクトグラフ自体の中に隠されています。プロパティにアクセスすると、結果を返す前にSQLクエリが実行される可能性があります。
foreach (var line in order.OrderLines)
{
total += line.Price;
}
長所:データアクセスの「いつ、どこで、どのように」は、ドメインロジックに焦点を当てた開発者から隠されています。データのロードを処理するコードは、集計に含まれていません。ロードされるデータの量は、コードが必要とする正確な量になる場合があります。
短所:パフォーマンスの問題に遭遇した場合、一般的な「1つのサイズですべてに対応する」ソリューションがあると、修正が困難です。遅延読み込みは全体的なパフォーマンスを低下させる可能性があり、遅延読み込みの実装は注意が必要です。
役割インターフェイス/熱心なフェッチ
各ユースケースは、集約クラスによって実装されたロールインターフェースを介して明示的になり、ユースケースごとにデータロード戦略を処理できるようになります。
フェッチ戦略は次のようになります。
public class BillOrderFetchingStrategy : ILoadDataFor<IBillOrder, Order>
{
Order Load(string aggregateId)
{
var order = new Order();
order.Data = GetOrderLinesWithPrice(aggregateId);
return order;
}
}
次に、集計は次のようになります。
public class Order : IBillOrder
{
void BillOrder(BillOrderCommand command)
{
foreach (var line in this.Data.OrderLines)
{
total += line.Price;
}
etc...
}
}
BillOrderFetchingStrategyを使用して集約を作成し、集約がその作業を行います。
長所:ユースケースごとにカスタムコードを使用できるため、最適なパフォーマンスを実現できます。インターフェース分離の原則に沿っています。複雑なコード要件はありません。集約ユニットテストは、読み込み戦略を模倣する必要はありません。一般的なローディング戦略はほとんどの場合に使用でき(「すべてロード」戦略など)、必要に応じて特別なローディング戦略を実装できます。
短所:ドメインコードを変更した後も、開発者はフェッチ戦略を調整/確認する必要があります。
フェッチ戦略アプローチを使用しても、ビジネスルールの変更に合わせてカスタムフェッチコードを変更していることに気付く場合があります。これは懸念事項を完全に分離するものではありませんが、最終的には保守しやすくなり、最初のオプションよりも優れています。フェッチ戦略は、HOW、WHEN、およびWHEREデータがロードされることをカプセル化します。1つのサイズがすべての遅延読み込みアプローチに適合するような柔軟性を失うことなく、問題をより適切に分離します。