我有一个现有的银行应用程序类,如下所示。银行帐户可以是SavingsBankAccount或FixedBankAccount。有一个名为IssueLumpSumInterest的操作。对于FixedBankAccount,仅当帐户的所有者没有其他帐户时才需要更新余额。
这要求FixedBankAccount对象了解帐户所有者的其他帐户。如何按照 SOLID / DDD / GRASP /信息专家模式执行此操作?
namespace ApplicationServiceForBank
{
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 IssueLumpSumInterest(int acccountID)
{
RepositoryLayer.BankAccount oneOfRepositroyAccounts = accountRepository.FindByID(p => p.BankAccountID == acccountID);
int ownerID = (int) oneOfRepositroyAccounts.AccountOwnerID;
IEnumerable<RepositoryLayer.BankAccount> 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);
}
}
}
阅读
实现业务逻辑(LINQ to SQL) http://msdn.microsoft.com/en-us/library/bb882671.aspx
构建LINQ to SQL应用程序
使用LINQ to SQL探索N层体系结构 http://randolphcabral.wordpress.com/2008/05/08/exploring-n-tier-architecture-with-linq-to-sql-part-3-of-n/
Domain Driven Design (Linq to SQL) - How do you delete parts of an aggregate?
答案 0 :(得分:2)
我注意到的第一件事是银行账户工厂的使用不当。存储库应该使用工厂来创建基于从数据存储中检索的数据的实例。因此,对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,以便在将来添加支持此行为的其他帐户类型时,使代码更易于维护和扩展。
答案 1 :(得分:2)
从阅读您的要求,我就是这样做的:
//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<Guid> _accountIds;
public IEnumerable<Guid> 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
}
}
答案 2 :(得分:1)
2分钟扫描回答..
一般的想法似乎是正确的。该服务封装了此事务的逻辑 - 而不是域对象。如果此更改,则只需更新一个位置。
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
}
}
答案 3 :(得分:0)
让我感到奇怪的事情:
IBankAccount
有方法FreezeAccount
,但我认为所有帐户的行为都非常相似?也许有一个BankAccount
类可以实现一些接口吗?AccountStatus
应该是一个枚举?如果帐户是“Forzen”会怎么样?