DDD(Domain Driven Design)
DP(Domain Primitive)
define:
一切模型、方法、架构的基础,是指在特定领域、拥有精准定义、可以自我验证、拥有行为的对象,可以认为是领域的最小组成部分
三条原则:
- 让隐性的概念显性化
- 让隐性的上下文显性化
- 封装多对象行为
Entity
-
DP: 抽象并封装自检和一些隐性属性的计算逻辑,且这些逻辑是无状态的
-
Entity: 抽象并封装单对象有状态的逻辑
-
Domain Service: 抽象并封装多对象的有状态逻辑
-
Repository: 抽象并封装外部数据访问逻辑
流程
- 首先对需要处理的业务问题进行总览
- 然后领域对象(Entity)进行划分,明确每个领域对象的包含的信息和职责边界,并进行跨对象、多对象的逻辑组织(Domain Serivice)
- 接着在上层应用中根据业务描述去编排Entity和Domain Service
- 最后进行一些下水道工作,对下层数据进行访问、RPC调用等一些具体实现
聚合(DDD-Aggregate)
define:
- 根
- 边界
使用:
- 运行时->domain service
- 持久化->repository
eg.
//标识符接口
public interface Identifier extends Serializable{}
//聚合根接口,实现此接口即为该边界内的聚合根
public interface AggregateRoot<T extends Identifier > extends Entity<T>{}
//实体类接口,实现此接口即为该边界内与聚合根关联的实体,此实体标识符为类型T
public interface Entity<T extends Identifier>{}
//以下为示例
————————————————————————————————————————————————————
domain service----------->| |
| 账号(根对象)----------->钱包----------->余额 |
repository----------->| |
————————————————————————————————————————————————————
//定义电话号码为聚合根对外接口
public class PhoneNumber implements Identifier{}
//定义聚合根实体类微信账户
public class WechatAccount implements AggregateRoot<PhoneNumber>{
//防止通过new构造对象,以使用工厂创建符合标准的对象
private WechatAccount(){}
private PhoneNumber id;
private NickName nickName;
private Wallet wallet;
// ...
public void withdraw(Asset asset){
Wallet.pay(asset);
}
public void deposit(Asset asset){
wallet.receive(asset);
}
}
//定义聚合根关联类微信钱包
public class Wallet implements Entity<WalletNumber>{
//防止通过new构造对象,以使用工厂创建符合标准的对象
private Wallet(){}
private WalletNumber id;
private Balance balance;
private Statement statement;
// ...
public void pay(Asset asset) throws BalanceException{
balance.decrease(asset.getBanlance);
statement.add(asset);
}
public void receive(Asset asset) throws BalanceException{
balance.increase(asset.getBanlance);
statement.add(asset);
}
}
//定义聚合根关联类微信钱包的关联类余额
public class Balance implements Entity<BalanceNumber>{
//防止通过new构造对象,以使用工厂创建符合标准的对象
private Balance(){}
private BalanceNumber id;
// ...
}
//定义Repository接口,其传递对象为聚合根
public interface Repository<T extends AggregateRoot<ID>, ID extends Identifier>{
void save(T t);
T find(ID id);
}
//定义AccountRepository接口,其传递对象为聚合根WechatAccount及其标识符PhoneNumber
public interface AccountRepository extends Repository<WechatAccount, PhoneNumber>{
void save(WechatAccount wechatAccount);
WechatAccount find(PhoneNumber id);
}
public class AccountRepositoryImpl implements AccountRepository{
private WechatAccountDAO wechatAccountDAO;
private WalletDAO walletDAO;
private BalanceDAO balanceDAO;
private StatementDAO statementDAO;
@Override
public void save(WechatAccount wechatAccount){
var wechatAccountPO = WechatAccountConverter.convert(wechatAccount);
wechatAccountDAO.save(wechatAccountPO);
//以下逻辑仍可优化
//即change tracking问题
var wallet = WalletFactory.obtain(wechatAccount);
var walletPO = WalletConverter.convert(wallet);
walletDAO.save(walletPO);
var balance = BalanceFactory.obtain(wallet);
var balancePO = BalanceConverter.convert(balance);
balanceDAO.save(balancePO);
var statement = StatementFactory.obtain(wallet);
var statementPO = StatementConverter.convert(wallet);
statementDAO.save(statementPO);
// ...
}
public WechatAccount find(PhoneNumber id){
// ...
}
}
//定义具体对外的服务接口TransferService
public interface TransferService{
void transfer(WechatAccount payer, WechatAccount payee, Asset asset);
}
//实现接口TransferService,操作entity
public class TransferServiceImpl implements TransferService{
public AccountRepository repository;
@Override
public void transfer(WechatAccount payer, WechatAccount payee, Asset asset){
payer.withdraw(asset);
payee.deposit(asset);
repository.save(payer);
repository.save(payee);
}
}
限界上下文(Bounded Context)
define:
上下文(Context)是业务目标,
限界(Bounded)则是保护和隔离上下文的边界,
使用BC目的是避免业务目标的不单一而带来的混乱与概念的不一致
特性:
- 最小完备(自身的职责 不需要与其他上下文相耦合)
- 自我履行(判断 职责是术语本单元 还是别的上下文所在单元 划分边界)
- 稳定空间(通过隐藏细节和开放抽象接口来封装变化)
- 独立进化(约束接口的规范与版本)