C# 如何使用DDD/CQR编写功能

C# 如何使用DDD/CQR编写功能,c#,.net,design-patterns,domain-driven-design,cqrs,C#,.net,Design Patterns,Domain Driven Design,Cqrs,我有一个银行帐户域,如下所示。可以有储蓄账户、贷款账户、固定账户等。一个用户可以有多个帐户。我需要添加一个新功能–获取用户的所有帐户。函数应该写在哪里,如何写 如果解决方案遵循固体原理(开闭原理,…)和DDD,那就太好了 任何可以使代码更好的重构都是受欢迎的 注意:AccountManipulator将由网站客户端通过web服务使用 namespace BankAccountBL { public class AccountManipulator { //Whether it shoul

我有一个银行帐户域,如下所示。可以有储蓄账户、贷款账户、固定账户等。一个用户可以有多个帐户。我需要添加一个新功能–获取用户的所有帐户。函数应该写在哪里,如何写

如果解决方案遵循固体原理(开闭原理,…)和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()
    {

    }
}
}
阅读:


  • 首先,你为什么需要会计?它完全不做任何事情,但会使代码更加复杂


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

    如果您的
    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);
    }
    
    公共类AccountManipulator
    {
    私人会计工厂(工厂),;
    私有用户存储库用户;
    公共AccountManipulator(AccountFactory工厂、UserRepository用户)
    {
    _工厂=工厂;
    _用户=用户;
    }
    公共帐户(int accountNumber)
    {
    var acc=_factory.GetAccount(accountNumber);
    根据冻结();
    }
    公共IEnumerable GetAccountsOf(用户){
    返回_users.getAccountId(user)。选择(_factory.GetAccount);
    }
    }
    公共接口用户存储库{
    IEnumerable GetAccountId(用户);
    }
    
    为了说明您的域是否可靠,您应该使用以下原则进行分析:

    • 单一责任:每个对象都有自己的责任(并且只有那一个):
      • AccountFactory:创建IAccounts
      • SavingsAccount:IAccount的实现,它从(数据库?web服务?)读取/写入数据
      • AccountManipulator:提供一组最小且简单的操作来处理域对象
    • 打开/关闭:您的类是否对扩展开放,对更改关闭?
      • AccountFactory:没有。如果您编写了IAccount的新实现,为了使用它,您必须更改AccountFactory。解决方案:抽象工厂
      • 储蓄账户?这取决于它是否将使用外部依赖项。需要更多的代码来说明
      • 会计:是的。如果需要对域对象执行其他操作,则可以直接使用其他服务,而无需更改AccountManipulator。或者你可以从中继承
    • Liskov替换:您能用另一个实现替换任何类吗?需要更多的代码来说明。您现在没有IAccount或IAccountFactory的其他实现
    • 依赖项反转:
      • AccountManufactor应该依赖于抽象:AccountFactory和UserRepository应该是接口

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

    如果
    AccountManipulator
    是一个Web服务,则此类将使用
    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);
       }
    
    }
    
    公共类AccountManipulator
    {
    公共账户(国际账户编号)
    {
    var repository=new AccountRepository();
    var account=repository.GetAccount(accountNr);
    账户冻结();
    保存(账户);
    }
    公共ICollection GetAccountsFruser(int userId)
    {
    var repository=new AccountRepository();
    return repository.getaccountsfourser(userId);
    }
    }
    
    首先,要真正回答您的问题,重要的是要知道为什么您需要获得所有用户帐户?你是:

  • 获取要在屏幕上显示的帐户列表以供用户使用 然后对单个帐户执行命令/事务
  • 对所有用户帐户执行单个命令/事务-例如“冻结所有用户帐户”
  • 我之所以问,是因为你只需要考虑DDD方面,如果后者是后者。如果此“功能”的原因是前者(在阅读了您的问题后,我怀疑是)-我真的建议只创建一个瘦查询服务层,获取屏幕所需的用户帐户数据。您不需要为此添加DDD的“限制”;不涉及任何事务或模型状态更改。提供此功能根本不需要涉及域模型。只需定义一些简单的POCO DTO,并使用实体框架获取数据并将其传递回UI

    这就是问题所在;您不需要存储库、工厂或聚合来为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:IAccountService
    {
    私人IAccountRepository\u accountRespository;
    public void FreezeAllAccountsForUser(Guid用户ID)
    {
    IEnumerable accounts=\u accountRespository.GetAccountsByUserId(userId);
    使用(IUnitOfWork unitOfWork=UnitOfWorkFactory.Create())
    {
    foreach(IAccount账户在_账户中)
    {
    账户冻结();
    _accountRespository.Save(account);
    }
    }
    }
    }
    
    其中AccountService是一个Web服务,即应用层

    总之,我的建议是:只有