DDD访问外部信息的方法

我有一个现有的银行应用程序类,如下所示。 银行账户可以是SavingsBankAccount或FixedBankAccount。 有一个名为IssueLumpSumInterest的操作。 对于FixedBankAccount,仅当帐户所有者没有其他帐户时才需要更新余额。

这要求FixedBankAccount对象了解帐户所有者的其他帐户。 如何通过遵循SOLID / DDD / GRASP /信息专家模式来做到这一点?

namespace ApplicationServiceForBank { public class BankAccountService { RepositoryLayer.IRepository accountRepository; ApplicationServiceForBank.IBankAccountFactory bankFactory; public BankAccountService(RepositoryLayer.IRepository repo, IBankAccountFactory bankFact) { accountRepository = repo; bankFactory = bankFact; } public void IssueLumpSumInterest(int acccountID) { RepositoryLayer.BankAccount oneOfRepositroyAccounts = accountRepository.FindByID(p => p.BankAccountID == acccountID); int ownerID = (int) oneOfRepositroyAccounts.AccountOwnerID; IEnumerable accountsForUser = accountRepository.FindAll(p => p.BankUser.UserID == ownerID); DomainObjectsForBank.IBankAccount domainBankAccountObj = bankFactory.CreateAccount(oneOfRepositroyAccounts); if (domainBankAccountObj != null) { domainBankAccountObj.BankAccountID = oneOfRepositroyAccounts.BankAccountID; domainBankAccountObj.AddInterest(); this.accountRepository.UpdateChangesByAttach(oneOfRepositroyAccounts); //oneOfRepositroyAccounts.Balance = domainBankAccountObj.Balance; this.accountRepository.SubmitChanges(); } } } 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; } } } namespace DomainObjectsForBank { public interface IBankAccount { int BankAccountID { get; set; } double Balance { get; set; } string AccountStatus { get; set; } void FreezeAccount(); void AddInterest(); } public class FixedBankAccount : IBankAccount { public int BankAccountID { get; set; } public string AccountStatus { get; set; } public double Balance { get; set; } public void FreezeAccount() { AccountStatus = "Frozen"; } public void AddInterest() { //TO DO: Balance need to be updated only if the person has no other accounts. Balance = Balance + (Balance * 0.1); } } } 

  1. 将Composition用于“is – a”关系的问题

  2. 实现业务逻辑(LINQ to SQL) http://msdn.microsoft.com/en-us/library/bb882671.aspx

  3. 构建LINQ to SQL应用程序

  4. 使用LINQ to SQL探索N层体系结构http://randolphcabral.wordpress.com/2008/05/08/exploring-n-tier-architecture-with-linq-to-sql-part-3-of-n/

  5. DTO(linq2sql)和Class对象之间的混淆!

  6. 域驱动设计(Linq to SQL) – 如何删除聚合的部分?

我注意到的第一件事是银行账户工厂的使用不当。 存储库应该使用工厂来创建基于从数据存储中检索的数据的实例。 因此,您对accountRepository.FindByID的调用将返回FixedBankAccount或SavingsBankAccount对象,具体取决于从数据存储返回的AccountType。

如果兴趣仅适用于FixedBankAccount实例,则可以执行类型检查以确保使用正确的帐户类型。

 public void IssueLumpSumInterest(int accountId) { var account = _accountRepository.FindById(accountId) as FixedBankAccount; if (account == null) { throw new InvalidOperationException("Cannot add interest to Savings account."); } var ownerId = account.OwnerId; if (_accountRepository.Any(a => (a.BankUser.UserId == ownerId) && (a.AccountId != accountId))) { throw new InvalidOperationException("Cannot add interest when user own multiple accounts."); } account.AddInterest(); // Persist the changes } 

注意:FindById应该只接受ID参数而不是lambda / Func。 您已通过名称“FindById”表示将如何执行搜索。 “accountId”值与BankAccountId属性进行比较的事实是隐藏在方法中的实现细节。 如果您想要使用lambda的通用方法,请将方法命名为“FindBy”。

如果所有实现都不支持该行为,我也不会将AddInterest放在IBankAccount接口上。 考虑一个单独的IInterestEarningBankAccount接口,它公开AddInterest方法。 我还会考虑在上面的代码中使用该接口而不是FixedBankAccount,以便在将来添加支持此行为的其他帐户类型时,使代码更易于维护和扩展。

从阅读您的要求,我将如何做到这一点:

 //Application Service - consumed by UI public class AccountService : IAccountService { private readonly IAccountRepository _accountRepository; private readonly ICustomerRepository _customerRepository; public ApplicationService(IAccountRepository accountRepository, ICustomerRepository customerRepository) { _accountRepository = accountRepository; _customerRepository = customerRepository; } public void IssueLumpSumInterestToAccount(Guid accountId) { using (IUnitOfWork unitOfWork = UnitOfWorkFactory.Create()) { Account account = _accountRepository.GetById(accountId); Customer customer = _customerRepository.GetById(account.CustomerId); account.IssueLumpSumOfInterest(customer); _accountRepository.Save(account); } } } public class Customer { private List _accountIds; public IEnumerable AccountIds { get { return _accountIds.AsReadOnly();} } } public abstract class Account { public abstract void IssueLumpSumOfInterest(Customer customer); } public class FixedAccount : Account { public override void IssueLumpSumOfInterest(Customer customer) { if (customer.AccountIds.Any(id => id != this._accountId)) throw new Exception("Lump Sum cannot be issued to fixed accounts where the customer has other accounts"); //Code to issue interest here } } public class SavingsAccount : Account { public override void IssueLumpSumOfInterest(Customer customer) { //Code to issue interest here } } 
  1. Account聚合上的IssueLumpSumOfInterest方法要求Customer聚合来帮助决定是否应该发布利息。
  2. 客户聚合包含帐户ID列表 – 而不是帐户聚合列表。
  3. 基类“帐户”具有多态方法 – FixedAccount检查客户没有任何其他帐户 – SavingsAccount不执行此检查。

2分钟扫描答案..

  • 不确定为什么需要BankAccount RepositoryLayer.BankAccountDomainObjectsForBank.IBankAccount的 2个表示。 隐藏连接的持久层…只处理服务中的域对象。
  • 不要传递/返回空 – 我认为是好建议。
  • finder方法看起来像LINQ方法,它从集合列表中选择项目。 你的方法看起来像是想得到第一个匹配并退出..在这种情况下你的参数可以是简单的原语(Ids)和lambdas。

一般的想法似乎是对的。 该服务封装了此事务的逻辑 – 而不是域对象。 如果此更改,则只有一个地方可以更新。

 public void IssueLumpSumInterest(int acccountID) { var customerId = accountRepository.GetAccount(accountId).CustomerId; var accounts = accountRepository.GetAccountsForCustomer(customerId); if ((accounts.First() is FixedAccount) && accounts.Count() == 1) { // update interest } } 

让我觉得奇怪的事情:

  • 您的IBankAccount有一个方法FreezeAccount ,但我认为所有帐户都会有非常相似的行为? 也许保证BankAccount类实现了一些接口?
  • AccountStatus应该是一个枚举? 如果一个帐户是“Forzen”会怎么样?