web-dev-qa-db-ja.com

いくつかのビジネスロジックをリポジトリに置くのではなく、完全にリポジトリから除外しますか?

(すべてではないにしても)ほとんどのビジネスロジックが独自のレイヤーに常駐する必要があることはわかっていますが、いくつかの基本的なビジネスロジックをリポジトリレイヤー自体の中に配置することの一般的なコンセンサスは何ですか?

私のシナリオ:オプションの列がいくつかあるテーブルがありますが、列の1つに値が含まれている場合、他の2つはその値の影響を受けます。

そのテーブルのエンティティのPOCOは次のようになります。

public class Template
{
     public int Id {get;set;}
     public string MessageTemplate {get;set;}
     public int? FacilityId {get;set;}
     public int? ResourceId {get;set;}
     public bit GlobalDefault {get;set;}
}

これで、グローバルデフォルトとして指定されたエントリは1つだけになり、同様に、各施設またはリソースはテーブルに1回だけリストされます。後者の制約はSQL Serverによって強制されますが、すべてがスムーズに流れるようにするには、ビジネスロジックが必要です。

このテーブルの場合、既存の施設またはリソースの新しいレコードを挿入しようとすると、以前のレコードが代わりに更新(または削除)されます。新しいグローバルデフォルトを設定する場合も同様です。 1つのエンティティのみをグローバルデフォルトにする必要があるため、別のエンティティが存在する場合は、代わりに削除または更新する必要があります。

明らかに、私のビジネスレイヤーはこれらのシナリオを可能な限り処理しますが、リポジトリ自体にそのレベルのロジックを実装することはどれほど不快なことでしょうか。

私のリポジトリでは次のようなものを想定しています:

public void InsertFacilityTemplate(Template template) 
{
    var existing = context.Templates.Where(n => n.FacilityId == template.FacilityId);
    Template recordToSave;
    if (existing.Count > 1) 
        DeleteRecords(existing);
    if (existing.Count == 1)
        recordToSave= existing.First();

    if (recordToSave != null)
    {
        recordToSave.MessageTemplate = template.MessageTemplate;
        Update(recordToSave);
    }
    else
        Insert(template);
}

ここで適切なアプローチは何ですか?

3
JD Davis

データベースとメソッドでは、OR存在に基づいて更新する)を挿入するアクションにupsertという用語を使用することがよくあります。リポジトリのupsertメソッド。

他の場所で挿入/更新ロジックについて繰り返しについて質問しているか、またはwhereについて質問しているかどうかは、説明から明確ではありませんこのロジック(つまり、メソッドの例)。

  • 前のステップまたは後続のステップでロジックを繰り返すことは、一般的に不適切な方法です。 このロジックが他の場所で繰り返される場合、エンドユーザーにエラー/警告メッセージを表示し、それ以上のアクションを防止するなど、別の影響があります。 「より多くのチェック=キャッチの可能性の向上」の目的で他の場所で処理することは、ロジックが意図的な処理ではなく希望によってまとめられていることを示しています。
  • あなたの例では、に関連するより多くの「懸念の分離」を使用することができますロジックのいくつかが行きます。 repoメソッドの名前は「InsertX」ですが、更新と削除も行います。ここでの削除は 副作用 であり、意図しない、または不明確な場合、これは実際には避けられます。 RemoveDuplicateInstances(ITemplate template)PrepareUpsert(string templateId)など、アップサートの準備をより明確にする別のメソッドを呼び出すことを検討してください。また、この例では更新されたフィールドが何であるかを知っていると想定しています。たとえば、GlobalDefault値が切り替えられた場合、または別のフィールドがクラスに追加された場合、Insertはそれらの変更を保持しますが、Updateへのロジックは保持しません。それは期待と矛盾した行動です。

これらの懸念は言われていますが、アプリケーションが小さい、概念実証、または非常に単純な場合、これらの変更は非常に小さいため、機能的には違いがありません。あなたの後に誰かが来たり、このコードベースを拡張し続けるつもりである場合、それは違います

2
joynoele

問題の根本は 貧血ドメインモデル のようです。データモデルを表すPOCOクラスを使用することに問題はありません。解決しようとしている問題は、次のビジネスルールに由来します。

  1. 「施設」は、その施設の各テンプレートが一意の「リソース」を参照している限り、複数のテンプレートを持つことができます。

  2. ファシリティのテンプレートのうち1つだけを「グローバルデフォルト」にする必要があります。

これらのルールを前提として、このファシリティを「施設」とそのすべての「テンプレート」を処理するクラスにプッシュする必要があります。

何かのようなもの:

var facility = // get by facility Id
var template = new Template() { /* initialize properties */ };

facility.SetGlobalTemplate(template);

次に、SetGlobalTemplateメソッドは、その機能に関連付けられたテンプレートをループし、それに応じてビットフラグを設定します。

次に、「リポジトリ」は新しい施設を保存するだけです。データマッピングが新しいテンプレートと以前のグローバルテンプレートへの更新を取得できることを確認してください。中途半端なORMはこれを実行できる必要があります(Entity Framework、NHibernateなど)。

repository.SaveFacility(facility);
0
Greg Burghardt