领域驱动设计学习总结(一):关于银行转账的思考

由于项目需要,在办公室抱着领域驱动设计这本书啃了一星期。今天突发奇想想写个学习总结。于是乎就拿前段时间大伙儿都在讨论的银行转账问题来练练手,第一次接触领域驱动设计,有不妥的地方请大伙多多指教。
一、问题描述
实现银行账号汇款功能。
核心业务:将账号A的若干资金转到账号B上。
设转账金额为M(下同)
Amount:账号资金
二、问题分析
1.账号A:账号A按照资金转出规则处理M
2.账号B:账号B按照资金转入规则处理M
3.系统:提供转账功能
4.转账规则:不收手续费,资金转出M时,Amount减少M;资金转入M时,Amount增加M.
5.对比该问题与调漆程序的区别:调漆时,两种油漆混合是得到第三种油漆,原有两种油漆不变;账号转账后,A、B两个账号都没发生改变,Amount发生改变。
三、解决思路
建立初步模型:
领域驱动设计学习总结(一):关于银行转账的思考
这个图画的蛮难看的。
相应代码如下(下午贴的那段代码,有些人应该看过):
public class AccountEntity { public string AccountId { get; set; } public Account CurrentAccount { get; set; } public void TransferTo(AccountEntity targetAccount, decimal Amount) { IRule _rule = new SimpleAccountRule(); CurrentAccount.Update(_rule.GetPayRule(Amount)); targetAccount.CurrentAccount.Update(_rule.GetIncomeRule(Amount)); } }
public class Account { public string AccountNO { get; set; } public string PassWord { get; set; } public decimal Amount{ get; set; } public void Update(decimal Amount) { this.Amount += Amount; } }
public interface IRule { decimal GetIncomeRule(decimal Amount); decimal GetPayRule(decimal Amount); } public class SimpleAccountRule : IRule { public decimal GetIncomeRule(decimal Amount) { return Amount; } public decimal GetPayRule(decimal Amount) { return (-1) * Amount; } }
public interface ITransferService { void TransferCash(AccountEntity sourceAccount, AccountEntity targetAccount, decimal Amount); } public class TransferService : ITransferService { public void TransferCash(AccountEntity sourceAccount, AccountEntity targetAccount, decimal Amount) { sourceAccount.TransferTo(targetAccount, Amount); } }
话说这东西出来的时候还挺满意的,但是看久了就觉得有点别扭了。
为什么我把Account独立定义了?画蛇添足啊!!!
对于Account来说,系统需要直接对其进行访问,而不需要通过对其它对象的遍历来获取Account,因此Account本身就是一个根,应该定义成Entity而不是Value Object。而对于多个Entity的操作,不应凡在Entity内部进行。于是乎对AccountEntity进行修改并引入AccountRepository。结果如下:
领域驱动设计学习总结(一):关于银行转账的思考
此时,AccountEntity的代码变为:
public class AccountEntity { public string AccountId { get; set; } public string AccountNO { get; set; } public string PassWord { get; set; } public decimal Amount { get; set; } public void Update(decimal Amount) { this.Amount += Amount; } }
Repository代码如下: public interface IAccountRepository { void TransferCash(AccountEntity sourceAccount, AccountEntity targetAccount, decimal Amount); } public class AccountRepository:IAccountRepository { public void TransferCash(AccountEntity sourceAccount, AccountEntity targetAccount, decimal Amount) { IRule _rule=new SimpleAccountRule(); sourceAccount.Update(_rule.GetPayRule(Amount)); targetAccount.Update(_rule.GetIncomeRule(Amount)); } }
TransferService代码如下:
public interface ITransferService { void TransferCash(AccountEntity sourceAccount, AccountEntity targetAccount, decimal account); } public class TransferService : ITransferService { public void TransferCash(AccountEntity sourceAccount, AccountEntity targetAccount, decimal Amount) { IAccountRepository _repository = new AccountRepository(); _repository.TransferCash(sourceAccount, targetAccount, Amount); } }
通过重构后,引入的Repository使得AccountEntity除了自身的Update事件外,不需要再去关心任何的外部变换。
六、最后的满足
最后,我们来关心下银行卡透支问题,假设账号A和账号B都不允许透支,因此,在转账前,必须对Amout与M进行比对。
引入声明类Specification,Specification代码如下
public interface ISpecification { bool IsSatisfy(AccountEntity account,decimal amount); } public interface AccountSpecification:ISpecification { public bool IsSatisfy(AccountEntity account,decimal amount) { return account.Amount>=amount; } }
此时,Repository的代码添加声明:
public void TransferCash(AccountEntity sourceAccount, AccountEntity targetAccount, decimal Amount) { ISpecification _specification=new AccountSpecification(); if(_specification.IsSatisfy(sourceAccount,Amount)) { IRule _rule=new SimpleAccountRule(); sourceAccount.Update(_rule.GetPayRule(Amount)); targetAccount.Update(_rule.GetIncomeRule(Amount)); } }
end.
Tags: 

延伸阅读

最新评论

发表评论