6

以下の銀行口座ドメインがあります。SavingsAccount、LoanAccount、FixedAccount などがあります。1 人のユーザーが複数のアカウントを持つことができます。ユーザーのすべてのアカウントを取得するという新しい機能を追加する必要があります。関数はどこに、どのように記述する必要がありますか?

ソリューションが SOLID 原則 (Open-Closed 原則など) と DDD に準拠していれば素晴らしいと思います。

コードを改善するリファクタリングは大歓迎です。

注: AccountManipulator は、Web サービスを介して Web サイト クライアントによって使用されます。

namespace BankAccountBL
{
public class AccountManipulator
{
    //Whether it should beprivate or public?
    private IAccount acc;

    public AccountManipulator(int accountNumber)
    {
        acc = AccountFactory.GetAccount(accountNumber);
    }

    public void FreezeAccount()
    {
        acc.Freeze();
    }

}

public interface IAccount
{
    void Freeze();
}

public class AccountFactory
{
    public static IAccount GetAccount(int accountNumber)
    {
        return new SavingsAccount(accountNumber);
    }
}

public class SavingsAccount : IAccount
{
    public SavingsAccount(int accountNumber)
    {

    }

    public void Freeze()
    {

    }
}
}

読む:

  1. CQRS 設計パターンを使用するのはいつですか?

  2. ドメイン駆動設計において、他のオブジェクトのリポジトリへの呼び出しをドメイン オブジェクトに置くことは DDD に違反しますか?

  3. レガシー システムのリポジトリにアクセスするドメイン ロジックのリファクタリング

  4. 次の例のうち、DDD の正しい使用法を表しているのはどれですか?

  5. 優れたドメイン駆動設計のサンプル

  6. オブジェクトごとに汎用リポジトリと特定のリポジトリを作成する利点は?

4

4 に答える 4

4

あなたAccountManipulatorがドメインのファサードである場合、コンストラクターにアカウント番号を入れません。私はこのようにリファクタリングします:

public class AccountManipulator
{
    private  AccountFactory _factory;
    private UserRepository _users;

    public AccountManipulator(AccountFactory factory, UserRepository users)
    {
       _factory = factory;
       _users = users;
    }

    public void FreezeAccount(int accountNumber)
    {
       var acc = _factory.GetAccount(accountNumber);
       acc.Freeze();
    }

    public IEnumerable<IAccount> GetAccountsOf(User user) {
       return _users.GetAccountIds(user).Select(_factory.GetAccount);
    }
}

public interface UserRepository {
    IEnumerable<int> GetAccountIds(User user);
}

ドメインが SOLID かどうかを判断するには、次の原則に従って分析する必要があります。

  • 単一の責任: すべてのオブジェクトには独自の責任があります (その 1 つだけ):
    • AccountFactory: IAccounts を作成します
    • SavingsAccount: (データベース? Web サービス?) から読み書きする IAccount の実装
    • AccountManipulator: ドメイン オブジェクトを処理する最小限の単純な操作セットを提供します。
  • オープン/クローズ: クラスは拡張に対してオープンで、変更に対してクローズですか?
    • AccountFactory: いいえ。IAccount の新しい実装を作成する場合、それを使用するには AccountFactory を変更する必要があります。解決策: 抽象工場
    • 貯蓄口座?外部依存関係を使用するかどうかによって異なります。より多くのコードが必要です。
    • AccountManipulator: はい。ドメイン オブジェクトで別の操作を行う必要がある場合は、AccountManipulator を変更せずに他のサービスを直接使用できます。または、それを継承することもできます
  • Liskov 置換: クラスを別の実装に置き換えることはできますか? より多くのコードが必要です。現在、IAccount または IAccountFactory の他の実装はありません
  • 依存関係の逆転:
    • AccountManipulator は抽象化に依存する必要があります: AccountFactory と UserRepository はインターフェースである必要があります。
于 2012-06-19T06:35:09.780 に答える
3

まず、あなたの質問に本当に答えるには、なぜすべてのユーザー アカウントを取得する必要があるのか​​を知ることが重要です。あなたは:

  1. ユーザーが単一のアカウントに対してコマンド/トランザクションを実行できるように、画面に表示するアカウントのリストを取得しますか?
  2. 「すべてのユーザー アカウントを凍結」など、すべてのユーザー アカウントに対して単一のコマンド/トランザクションを実行しますか?

私が尋ねる理由は、後者の場合にのみ DDD の側面を考慮する必要があるためです。この「機能」の理由が前者である場合 (質問を読んだ後、そうであると思われます) 、画面に必要なユーザーのアカウント データを取得するシン クエリ サービス レイヤーを作成することを強くお勧めしますこのために DDD の「制限」を追加する必要はありません。関連するトランザクションやモデル状態の変更はありません。この機能を提供するのに、ドメイン モデルを含める必要はまったくありません。シンプルな POCO DTO をいくつか定義し、Entity Framework を使用してデータを取得し、それを UI に戻すだけです。

これがCQRS の目的です。ユーザーが選択できるアカウントのリストを UI に提供するために、リポジトリ、ファクトリ、または集計は必要ありませ

ユーザーのすべてのアカウントに対して単一のトランザクションが必要なシナリオがある場合、次のようにします。

public class AccountService : IAccountService
{
    private IAccountRepository _accountRespository;

    public void FreezeAllAccountsForUser(Guid userId)
    {
        IEnumerable<IAccount> accounts = _accountRespository.GetAccountsByUserId(userId);

        using (IUnitOfWork unitOfWork = UnitOfWorkFactory.Create())
        {
            foreach (IAccount account in _accounts)
            {
                account.Freeze();
                _accountRespository.Save(account);
            }
        }
    }
}

AccountService は Web サービス、つまりアプリケーション層です。

要約すると、私のアドバイスは次のとおりです。トランザクションを必要とするコマンドのコンテキストでのみ DDD を検討してください。データのリストを取得するため。UI が使用できる単純なクエリ サービスを作成します。

PSあなたの質問といくつかの回答でFactoryパターンの誤用に気づきました。ファクトリは、特定のデータが与えられると、オブジェクトの作成戦略を提供するように設計されています。データベースを呼び出す「GetAccount(accountId)」メソッドがあってはなりません。リポジトリはデータベースを呼び出し、データをファクトリに渡してオブジェクトを作成します。

于 2012-06-19T09:00:32.280 に答える
1

まず、なぜ AccountManipulator が必要なのですか? それはまったく何もしませんが、コードをより複雑にします。

User のすべてのアカウントを取得する場合、このメソッドを配置する最も論理的な場所は User クラスです。そのメソッドにアカウント ファクトリを渡すことができます。その後の実装は、おそらくアカウントの保存方法によって異なります。

于 2012-06-19T06:30:58.647 に答える
0

「AccountFactory」の名前を に変更し、特定のユーザーのすべてのアカウントを取得するAccountRepositoryメソッドを追加します。GetAccountsForUser( int userId )

が Web サービスの場合AccountManipulator、このクラスは次のAccountRepositoryように を使用します。

public class AccountManipulator
{

   public void FreezeAccount( int accountNr )
   {
        var repository = new AccountRepository();
        var account = repository.GetAccount(accountNr);
        account.Freeze();
        repository.Save(account);        
   }

   public ICollection<Account> GetAccountsForUser( int userId )
   {
        var repository = new AccountRepository();
        return repository.GetAccountsForUser (userId);
   }

}
于 2012-06-19T08:09:47.707 に答える