6

我有一个如下所列的银行账户域。可以有 SavingsAccount、LoanAccount、FixedAccount 等。一个用户可以有多个帐户。我需要添加一个新功能——获取用户的所有帐户。函数应该写在哪里以及如何写?

如果解决方案遵循 SOLID 原则(开闭原则,...)和 DDD,那就太好了。

欢迎任何可以使代码更好的重构。

注意:AccountManipulator 将由网站客户端通过 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,您应该按照以下原则对其进行分析:

  • 单一职责:每个对象都有自己的职责(并且只有那个职责):
    • 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 是一个网络服务,即应用层。

总之,我的建议是:只在需要事务的命令上下文中考虑 DDD 。用于获取数据列表;创建一个UI 可以使用的简单查询服务。

PS我注意到您的问题和一些答案中滥用了工厂模式。工厂的设计目的是在给定特定数据的情况下提供对象的 CREATION 策略。不应该有调用数据库的“GetAccount(accountId)”方法;存储库调用数据库,然后将数据传递给工厂以创建对象。

于 2012-06-19T09:00:32.280 回答
1

首先,为什么需要 AccountManipulator?它完全没有任何作用,但会使代码更加复杂。

至于获取用户的所有帐户,放置此方法最合乎逻辑的位置是在用户类中。您可以将帐户工厂传递给该方法,进一步的实现可能取决于您存储帐户的方式。

于 2012-06-19T06:30:58.647 回答
0

我将重命名“AccountFactory”AccountRepository并在其中添加一个额外的方法,GetAccountsForUser( int userId )该方法检索特定用户的所有帐户。

如果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 回答