dp是一种基本类型,包括type(数据类型)和class(类)。
public class PhoneNumber {
private final String number;
public String getNumber() {
return number;
}
public PhoneNumber(String number) {
if (number == null) {
throw new ValidationException("number不能为空");
} else if (isValid(number)) {
throw new ValidationException("number格式错误");
}
this.number = number;
}
public String getAreaCode() {
for (int i = 0; i < number.length(); i++) {
String prefix = number.substring(0, i);
if (isAreaCode(prefix)) {
return prefix;
}
}
return null;
}
private static boolean isAreaCode(String prefix) {
String[] areas = new String[]{"0571", "021", "010"};
return Arrays.asList(areas).contains(prefix);
}
public static boolean isValid(String number) {
String pattern = "^0?[1-9]{2,3}-?\\d{8}$";
return number.matches(pattern);
}
}
如上例子,将于电话号相关的逻辑完整的收集到一个文件(class)中,形成了phoneNumber这个type
一个新应用在全国通过 地推业务员做推广,需要做一个用户注册系统,同时希望在用户注册后能够通过用户电话(先假设仅限座机)的地域(区号)对业务员发奖金。
传统代码:
public class User {
Long userId;
String name;
String phone;
String address;
Long repId;
}
public class RegistrationServiceImpl implements RegistrationService {
private SalesRepRepository salesRepRepo;
private UserRepository userRepo;
public User register(String name, String phone, String address)
throws ValidationException {
// 校验逻辑
if (name == null || name.length() == 0) {
throw new ValidationException("name");
}
if (phone == null || !isValidPhoneNumber(phone)) {
throw new ValidationException("phone");
}
// 此处省略address的校验逻辑
// 取电话号里的区号,然后通过区号找到区域内的SalesRep
String areaCode = null;
String[] areas = new String[]{"0571", "021", "010"};
for (int i = 0; i < phone.length(); i++) {
String prefix = phone.substring(0, i);
if (Arrays.asList(areas).contains(prefix)) {
areaCode = prefix;
break;
}
}
SalesRep rep = salesRepRepo.findRep(areaCode);
// 最后创建用户,落盘,然后返回
User user = new User();
user.name = name;
user.phone = phone;
user.address = address;
if (rep != null) {
user.repId = rep.repId;
}
return userRepo.save(user);
}
private boolean isValidPhoneNumber(String phone) {
String pattern = "^0[1-9]{2,3}-?\\d{8}$";
return phone.matches(pattern);
}
}
DDD架构代码:
public class PhoneNumber {
private final String number;
public String getNumber() {
return number;
}
public PhoneNumber(String number) {
if (number == null) {
throw new ValidationException("number不能为空");
} else if (isValid(number)) {
throw new ValidationException("number格式错误");
}
this.number = number;
}
public String getAreaCode() {
for (int i = 0; i < number.length(); i++) {
String prefix = number.substring(0, i);
if (isAreaCode(prefix)) {
return prefix;
}
}
return null;
}
private static boolean isAreaCode(String prefix) {
String[] areas = new String[]{"0571", "021", "010"};
return Arrays.asList(areas).contains(prefix);
}
public static boolean isValid(String number) {
String pattern = "^0?[1-9]{2,3}-?\\d{8}$";
return number.matches(pattern);
}
}
public class User {
UserId userId;
Name name;
PhoneNumber phone;
Address address;
RepId repId;
}
public User register(
@NotNull Name name,
@NotNull PhoneNumber phone,
@NotNull Address address
) {
// 找到区域内的SalesRep
SalesRep rep = salesRepRepo.findRep(phone.getAreaCode());
// 最后创建用户,落盘,然后返回,这部分代码实际上也能用Builder解决
User user = new User();
user.name = name;
user.phone = phone;
user.address = address;
if (rep != null) {
user.repId = rep.repId;
}
return userRepo.saveUser(user);
}
调用时:
public User register(String, String, String)
传统代码可能会出现如下错误:
service.register("张三", "北京", "0731-88888888")
这个错误是编译无法发现的
DDD架构代码:
public User register(Name, PhoneNumber, Address);
service.register(new Name("张三"), new Address("北京"), new PhoneNumber("0731-88888888"))
public User register(
@NotNull Name name,
@NotNull PhoneNumber phone,
@NotNull Address address
)
DP参数一定是正确的或null, 将数据验证前置到了调用方
DDD只剩下核心业务逻辑
解决第三方不可控、入参出参强耦合的问题,最常用的设计模式是防腐层
防腐层原则是:外部一切不可信
作用:
用Domain Primitive封装跟实体无关的无状态计算逻辑
用Entity封装单对象的有状态的行为,包括业务校验
业务逻辑清晰,数据存储和业务逻辑完全分隔。
Entity、Domain Primitive、Domain Service都是独立的对象,没有任何外部依赖,但是却包含了所有核心业务逻辑,可以单独完整测试。
原有的TransferService不再包括任何计算逻辑,仅仅作为组件编排,所有逻辑均delegate到其他组件。这种仅包含Orchestration(编排)的服务叫做Application Service(应用服务)。
repository是对聚合根进行操作,一个聚合根可能包括多个entity,当其中1个entity改变其他不改变时,需要找到这个改变的entity,即变更追踪。
有一下2个主流方法:
1.基于snapshot
当数据从DB里取出来后,在内存中保存一份snapshot,然后在数据写入时和snapshot比较。
2.基于proxy
传统OOP代码问题:
声明:本文是个人学习笔记
参考自:
https://zhuanlan.zhihu.com/p/340911587
https://zhuanlan.zhihu.com/p/343388831
https://zhuanlan.zhihu.com/p/348706530
https://zhuanlan.zhihu.com/p/356518017