0

次のコードを持つ BusinessLayer プロジェクトがあります。ドメイン オブジェクトは FixedBankAccount (IBankAccount を実装) です。

  1. リポジトリは、ドメイン オブジェクトのパブリック プロパティとして作成され、インターフェイス メンバとして作成されます。リポジトリがインターフェースメンバーにならないようにリファクタリングする方法は?

  2. ドメイン オブジェクト (FixedBankAccount) は、リポジトリを直接利用してデータを保存します。これは単一責任の原則に違反していますか? それを修正する方法は?

注: リポジトリ パターンは、LINQ to SQL を使用して実装されています。

編集

次のコードはより良いアプローチですか? https://codereview.stackexchange.com/questions/13148/is-it-good-code-to-satisfy-single-responsibility-principle

コード

public interface IBankAccount
{
    RepositoryLayer.IRepository<RepositoryLayer.BankAccount> AccountRepository { get; set; }
    int BankAccountID { get; set; }
    void FreezeAccount();
}


public class FixedBankAccount : IBankAccount
{
    private RepositoryLayer.IRepository<RepositoryLayer.BankAccount> accountRepository;
    public RepositoryLayer.IRepository<RepositoryLayer.BankAccount> AccountRepository
    {
        get
        {
            return accountRepository;
        }
        set
        {
            accountRepository = value;
        }
    }

    public int BankAccountID { get; set; }

    public void FreezeAccount()
    {
        ChangeAccountStatus();
    }

    private void SendEmail()
    {

    }

    private void ChangeAccountStatus()
    {
        RepositoryLayer.BankAccount bankAccEntity = new RepositoryLayer.BankAccount();
        bankAccEntity.BankAccountID = this.BankAccountID;

        accountRepository.UpdateChangesByAttach(bankAccEntity);
        bankAccEntity.Status = "Frozen";
        accountRepository.SubmitChanges();
    }
}


public class BankAccountService
{
    RepositoryLayer.IRepository<RepositoryLayer.BankAccount> accountRepository;
    ApplicationServiceForBank.IBankAccountFactory bankFactory;

    public BankAccountService(RepositoryLayer.IRepository<RepositoryLayer.BankAccount> repo, IBankAccountFactory bankFact)
    {
        accountRepository = repo;
        bankFactory = bankFact;
    }

    public void FreezeAllAccountsForUser(int userId)
    {
        IEnumerable<RepositoryLayer.BankAccount> accountsForUser = accountRepository.FindAll(p => p.BankUser.UserID == userId);
        foreach (RepositoryLayer.BankAccount repositroyAccount in accountsForUser)
        {
            DomainObjectsForBank.IBankAccount acc = null;
            acc = bankFactory.CreateAccount(repositroyAccount);
            if (acc != null)
            {
                acc.BankAccountID = repositroyAccount.BankAccountID;
                acc.accountRepository = this.accountRepository;
                acc.FreezeAccount();
            }
        }
    }
}


public interface IBankAccountFactory
{
     DomainObjectsForBank.IBankAccount CreateAccount(RepositoryLayer.BankAccount repositroyAccount);
}


public class MySimpleBankAccountFactory : IBankAccountFactory
{
    public DomainObjectsForBank.IBankAccount CreateAccount(RepositoryLayer.BankAccount repositroyAccount)
    {
        DomainObjectsForBank.IBankAccount acc = null;

        if (String.Equals(repositroyAccount.AccountType, "Fixed"))
        {
            acc = new DomainObjectsForBank.FixedBankAccount();
        }

        if (String.Equals(repositroyAccount.AccountType, "Savings"))
        {
            acc = new DomainObjectsForBank.SavingsBankAccount();
        }

        return acc;
    }
}


読む:

  1. DDD - エンティティの状態遷移

  2. https://codereview.stackexchange.com/questions/13148/is-it-good-code-to-satisfy-single-responsibility-principle

  3. 「Single Responsibility Principle」を使用すると、コンテナーにパブリック セッターが強制されます

  4. https://softwareengineering.stackexchange.com/questions/150760/single-responsibility-principle-how-can-i-avoid-code-fragmentation

4

4 に答える 4

3

リポジトリがインターフェイス メンバーにならないようにこのコードをリファクタリングするのは簡単です。リポジトリは、インターフェイスではなく、実装の依存関係です。具体的なクラスに挿入し、IBankAccount から削除します。

public class FixedBankAccount : IBankAccount
{
    public FixedBankAccount(RepositoryLayer.IRepository<RepositoryLayer.BankAccount> accountRepository)
    {
        this.accountRepository = accountRepository;
    }

    private readonly RepositoryLayer.IRepository<RepositoryLayer.BankAccount> accountRepository;

    public int BankAccountID { get; set; }
    public void FreezeAccount()
    {
         ChangeAccountStatus();
    }

    private void SendEmail()
    {
    }

    private void ChangeAccountStatus()
    {
        RepositoryLayer.BankAccount bankAccEntity = new RepositoryLayer.BankAccount();
        bankAccEntity.BankAccountID = this.BankAccountID;

        accountRepository.UpdateChangesByAttach(bankAccEntity);
        bankAccEntity.Status = "Frozen";
        accountRepository.SubmitChanges();
    }

}

2番目の質問に関しては...

はい、ドメイン オブジェクトは永続化コードを認識することで SRP に違反しています。ただし、これは問題になる場合とそうでない場合があります。多くのフレームワークは、これらの責任を組み合わせて大きな効果を上げています。たとえば、Active Record パターンです。IRepository をモックする必要があるという点で、単体テストが少し興味深いものになります。

より永続的で無知なドメインを使用することを選択した場合は、おそらく Unit of Work パターンを実装するのが最善でしょう。ロード/編集/削除されたインスタンスは Unit of Work に登録され、トランザクションの終了時に変更を保持します。作業単位は、変更の追跡を担当します。

この設定方法は、作成しているアプリケーションの種類と使用しているツールによって異なります。たとえば、Entity Framework を使用する場合、DataContext を作業単位として使用できると思います。(Linq-to-SQL にも DataContext の概念がありますか?)

Entity Framework 4 を使用した Unit of Work パターンの例を次に示します

于 2012-06-27T18:43:18.420 に答える
3

そもそもアンチパターンはパターンであると想定されているため(認識可能で広く普及している方法)、「Repository-in-the」については知りません。 -ドメインオブジェクト」パターン。

ただし、BankAccount ドメイン オブジェクトには 3 つの責任が混在しているため、IMO の悪い習慣であることは確かです。

  • 自分自身をフリーズさせ、そのステータスを変更するという、ドメイン オブジェクトとしての自然で正当な責任。

  • (accountRepository を使用して) 永続的なストアを更新し、変更を送信する責任。

  • メッセージの送信方法 (この場合は電子メール) を決定し、送信する責任。

その結果、Domain オブジェクトはあまりにも多くのものと密接に結びついており、硬直的で壊れやすいものになっています。あまりにも多くの理由で変更されたり、壊れたりする可能性があります。

したがって、アンチパターンはありませんが、単一責任の原則に違反していることは確かです。

最後の 2 つの責任は、別のオブジェクトに移動する必要があります。変更の送信は、ビジネス トランザクション (作業単位) を管理するオブジェクトに属し、トランザクションを終了して物事をフラッシュする適切なタイミングを認識しています。2 つ目は、インフラストラクチャ レイヤーの EmailService に配置できます。理想的には、グローバル Freeze 操作を行うオブジェクトは、メッセージ配信メカニズム (メールまたはその他による) を認識するべきではありませんが、代わりにそれを挿入する必要があります。これにより、柔軟性が向上します。

于 2012-06-27T12:42:18.593 に答える
1

インターフェイスは、単一責任の原則とは直接関係ありません。データ アクセス コードをビジネス ロジックから完全に分離することはできません。ある時点で通信する必要があります。あなたがしたいことは、これが発生する場所を最小限に抑えることです(ただし、避けないでください)。データベース スキーマが物理的ではなく論理的であること(つまり、テーブルや列ではなく述語に基づくこと) と、実装ベースのコード (データベース管理システム接続ドライバーなど) が1か所 (データベース管理システム接続ドライバーなど) にあることを確認してください。データベース。各エンティティは、 1 つのクラスで表す必要があります。それでおしまい。

于 2012-07-01T10:06:58.040 に答える
1

Remiのソリューションははるかに優れていますが、IMOのより良いソリューションは次のとおりです。

1- ドメイン オブジェクトには何も注入 しないでください。ドメイン エンティティには何も注入する必要はありません。サービスではありません。リポジトリではありません。何もない。純粋なドメイン モデルの良さ

2- サービス レイヤーがリポジトリに SubmitChanges を実行するように指示します... ただし、サービス レイヤーはシンである必要があり、ドメイン オブジェクトは貧血であってはならないことに注意してください

于 2012-06-30T12:23:52.743 に答える