責任とORMの分離の原則

ORMのコンテキストで懸念の分離(SoC)の原則を議論し、この原則がなぜそれほど重要なのかを説明したいと思います。 また、ドメインロジックとデータストレージロジック間の責任の境界違反の例も検討します。



責任分担の原則



各アプリケーションでは、いくつかの懸念事項を扱っています。 通常、UI、ビジネスロジック、データベースの少なくとも3つが明確に定義されています。 責任分離の原則は、単一責任の原則(単一責任原則、SRP)と密接に関連しています。 SoCは、単一のクラスではなく、アプリケーション全体に適用されるSRPと考えることができます。 ほとんどの場合、これら2つの原則は同じ意味で使用できます。



ORMの場合、SoCの原則は、ドメイン(ドメイン)ドメインロジックとデータベース内のデータストレージロジックの分離に関連しています。 ドメイン内のドメインクラスがデータベースにどのように格納されているかを知らない場合、アプリケーションコードの責任は十分に分離されていると言えます。 もちろん、これら2つのアプリケーション領域を完全に分離することは常に可能とは限りません。 パフォーマンスの要件により、これらの境界を破らなければならない場合があります。 ただし、いずれにしても、可能な限り完全に責任を明確にするように常に努力する必要があります。



そしてもちろん、データベースのデータストレージロジックからアプリケーションドメインロジックを分離することはできません。それらを接続するものが必要です。 これがORMが私たちを助ける場所です。 ORMは、ドメインモデルコードとデータベースの間のメディエーターとして機能します。 ほとんどの場合、ORMは、ドメインコードもデータベースも互いを認識しないような方法でこれを行うことができます。



画像








SoCが重要な理由



責任の分離を適切に維持する方法に関する多くの情報があります。 しかし、なぜこれが重要なのでしょうか?



画像








単一のクラスでさまざまな責任を維持しながら、このクラス内の各操作と同時にその一貫性を維持する必要があります。 これはすぐに組み合わせ爆発につながります。 さらに、アプリケーションの複雑さは、ほとんどの開発者が考えるよりもずっと速く成長しています。 責任が追加されるたびに、クラスの複雑さが桁違いに増加します。



このすべての複雑さに対処するには、これらの責任を共有する必要があります。



画像








SoCは、単に優れたコードや美しいコードの問題ではありません。 SoCの原則は、許容可能な開発速度を維持するために不可欠です。 さらに、プロジェクトの成功にとって重要です。



人は、一度に9個までのオブジェクトを短期記憶に保持できます 。 責任の明確な分離がないアプリケーションは、さまざまな共有されていない概念が相互に作用する可能性がある膨大な組み合わせのために、開発者の短期記憶を非常に迅速にオーバーフローさせます。



これらの概念を高度に関連する部分に分割すると、開発中のアプリケーションを「分割して征服」できます。 アプリケーションの残りの部分と疎結合された小さな分離されたコンポーネントの複雑さを管理するのははるかに簡単です。



データベースにデータを保存するロジックがドメインロジックに浸透する場合



データストレージのロジックがドメインのドメインを貫通する例を見てみましょう。



例1:ドメインモデルクラス内のオブジェクトの永続ステータスの操作。



public void DoWork(Customer customer, MyContext context) { if (context.Entry(customer).State == EntityState.Modified) { // Do something } }
      
      





オブジェクトの現在の永続的な状態(つまり、データベースに既に存在するかどうか)は、ドメインモデルのロジックとは関係ありません。 理想的には、ドメインオブジェクトは、アプリケーションのビジネスロジックに直接関連するデータのみを操作する必要があります。



例2:識別子を操作する



 public void DoWork(Customer customer1, Customer customer2) { if (customer1.Id > 0) { // Do something } if (customer1.Id == customer2.Id) { // Do something } }
      
      





ドメインクラスで識別子を使用することは、おそらく最も一般的なタイプのさまざまなタイプのアプリケーションの責任の混合です。 識別子は、データベースにオブジェクトを保存する方法の実装詳細です 。 原則として、それらはオブジェクト同士を比較するために使用されます。 これらをこの目的にも使用する場合、ドメインオブジェクトの基本クラスの等値メンバーをオーバーライドし、「customer1.Id == customer2.Id」ではなく「customer1 == customer2」と記述すると、はるかに優れたソリューションになります。



例3:永続属性によるドメインクラスのプロパティの分離



 public class Customer { public int Number { get; set; } public string Name { get; set; } //    ,       public string Message { get; set; } }
      
      





そのようなコードを書く傾向がある場合は、停止してドメインモデルを検討する必要があります。 同様のアプローチは、ドメインモデルに関連しない要素を含めたことを示している場合があります。



ドメインロジックがデータベースに入るとき



例1:カスケード削除



カスケード削除のためのデータベースのセットアップは、データストレージロジックへのドメインロジックの浸透の一例です。 理想的には、データベース自体に、データ削除がトリガーされるタイミングに関する情報を含めないでください。 このような知識はドメインケアです。 C#/ Java / etcコードは、そのようなロジックを格納する唯一の場所である必要があります。



例2:ストアドプロシージャ



別の例として、データベース内のデータを変更するストアドプロシージャの使用があります。 ドメインロジックがデータベースに侵入することを許可しないでください。ドメインモデルのデータの状態を変更するコードを保存してください。



ここには2つのポイントがあります。 まず、ほとんどの場合、データベース内のデータを変更しないストアドプロシージャ(読み取り専用のストアドプロシージャ)を使用しても問題はありません。 副作用を引き起こすコードをドメインモデルに配置し、副作用のないコードをストアドプロシージャに配置することは、 CQRSの原則と完全に一致しています。



第二に、SQLの使用を避けられない場合があります。 たとえば、何らかの理由でオブジェクトのグループを削除する必要がある場合、SQL DELETEステートメントを使用すると、この作業がはるかに高速になります。 このような場合、データベース内のデータを変更するSQLの使用は正当化されますが、そのような例外はすべて厳密に制御し、成長を許可しないようにする必要があります。



例3:デフォルト値



データベーステーブルのデフォルト値は、データベースに侵入したドメインロジックの別の例です。 ドメインエンティティがデフォルトで持っているプロパティの値は、コードで定義され、DBに与えられるべきではありません。



そのような知識をアプリケーションのさまざまな場所から断片的に収集することがどれほど難しいかを考えてください。 1つの場所に保存する方がはるかに簡単です。



おわりに



これらの「リーク」のほとんどは、人々が自分のアプリケーションをサブジェクトエリアの観点ではなく、データの観点で考えるという事実のために発生します。 多くの開発者は、この方法で開発しているシステムを検討しています。 それらにとって、クラスはデータベースからUIに転送するデータの単なるリポジトリであり、ORMはSQLクエリの結果からこれらのオブジェクトにデータを手動でコピーしないためのユーティリティです。 思考のパラダイムを変えることはしばしば困難です。 しかし、それが行われた後、人々は通常、特に大規模なプロジェクトでアプリケーションをより迅速に開発できるようにする表現ドメインモデルの世界を開きます。



もちろん、アプリケーションコード内で責任の分離の望ましいレベルを達成することは常に可能ではありません。 しかし、ほとんどの場合、これを妨げるものはありません。 ほとんどのプロジェクトは、技術的な要件を満たすことができないため失敗します。 ほとんどが失敗するのは、開発者がその中の何かを変更するのを妨げる乱雑なコードの塊に埋もれているためです。 このようなコードの各変更は、アプリケーション全体にバグのカスケードと予期しない副作用をもたらします。



SoCは、このような結果を回避する原則です。



元の記事へのリンク: ORMの懸念の分離



All Articles