由于项目需要,在办公室抱着领域驱动设计这本书啃了一星期。今天突发奇想想写个学习总结。于是乎就拿前段时间大伙儿都在讨论的银行转账问题来练练手,第一次接触领域驱动设计,有不妥的地方请大伙多多指教。

一、问题描述

实现银行账号汇款功能。

核心业务:将账号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.

作者: 菜鸟老了 发表于 2011-07-08 17:09 原文链接

推荐.NET配套的通用数据层ORM框架:CYQ.Data 通用数据层框架