封装是面向对象的的第一大特性,属性私有化,根据提供的setter和getter方法来访问属性,隐藏具体属性和实现细节。
@Data
public class UserInfoDomain {
private String name;
private String IDCard;
}
行为变成简单的setter/getter方法了,一个UserInfo其实有很多行为,例如:新增、手机号是否黑名单、IDCard是否合法等,但是这些行为代码就写满了service方法。
领域模型定义为【结合了行为和数据的领域对象模型】,与Entity是明显不同的,Entity仅是存储在数据库中的数据的对象表示,而行为位于单独的类中。领域模型基础会分为失血模型、贫血模型、充血模型、胀血模型,下面一一列举:
失血模型
Domain Object只有属性的get/set方法的纯数据类,所有行为的业务逻辑完全由Service层来完成。
Request代码
@Data
public class UserInfoRequest {
private String name;
private String IDCard;
}
用户领域对象
@Data
public class UserInfoDomain {
private String name;
private String IDCard;
} }
应用层代码
@RestController
public class UserInfoController {
@Autowired
private UserInfoService userInfoService;
@PostMapping(/user)
public ResultData regist(UserInfoRequest request){
UserInfoDomain userInfoDomain = BeanUtils.copyObject(request , UserInfoDomain.class);
userInfoService.addUser(userInfoDomain);
return ResultData.getSuccess();
}
}
业务逻辑层代码
@Service
public class UserInfoService {
@Autowired
private UserInfoRepository userInfoRepository;
public UserInfoDomain get(String idCade) {
return userInfoRepository.get(idCade);
}
public void addUser(UserInfoDomain userInfoDomain) {
if (null != userInfoRepository.get(userInfoDomain.getIDCard())) {
throw new BusinessException(BizCode.IDCard_IS_EXIST);
}
//身份证号合法性校验
String IDStr = userInfoDomain.getIDCard();
if (IDStr.length() != 18) {
throw new BusinessException(BizCode.IDCard_ILLEGAL);
}
if (IDStr.length() == 18) {
Ai = IDStr.substring(0, 17);
} else if (IDStr.length() == 15) {
Ai = IDStr.substring(0, 6) + "19" + IDStr.substring(6, 15);
}
// 判断出生年月是否有效
String strYear = Ai.substring(6, 10);// 年份
String strMonth = Ai.substring(10, 12);// 月份
String strDay = Ai.substring(12, 14);// 日期
if (isDate(strYear + "‐" + strMonth + "‐" + strDay) == false)
{
log.info("身份证号:{} 日期不正确",IDStr);
throw new BusinessException(BizCode.IDCard_ILLEGAL);
}
......
Date birthday = DateUtil.toDate(IDStr.substring(6, 14));//生日
String sex = IDStr.substring(IDStr.length() ‐2 , IDStr.le ngth() ‐1);//性别
int age = DateUtil.getAge(birthday);//年龄
userInfoDomain.setBirthday(birthday);
userInfoDomain.setSex(sex == 1 ? "M" : "F";);
userInfoDomain.setAge(age);
userInfoDomain.setStatus(INIT);
//注册限制校验
if(this.age < 18 || this.age > 80){
throw new BusinessException(BizCode.NOT_QUALIFIED);
}
if(this.sex.equals("F")){
throw new BusinessException(BizCode.NOT_QUALIFIED);
}
//添加用户
userInfoRepository.add(userInfoDomain);
}
}
仓储层代码
@Component
public class UserInfoRepository implements Repository<UserInfoDo main>{
@Override
public void add(UserInfoDomain model) {
UserInfoEntity userInfoEntity = new UserInfoEntity();
userInfoEntity.setIDCard(model.getIDCard());
......
userInfoMapper.insert(userInfoEntity);
}
@Override
public UserInfoDomain get(String idCard) {
UserInfoEntity userInfoEntity = userInfoMapper.get(idCard);
if(null == userInfoEntity){
return null;
}
UserInfoDomain model = new UserInfoDomain();
model.setName(userInfoEntity.getName());
model.setAge(userInfoEntity.getAge());
model.setSex(userInfoEntity.getSex());
model.setBirthday(userInfoEntity.getBirthdady());
}
}
以上就是大家常见的代码,Controller-->Service-->Repository-->Mapper, ProductService 中存在大量的所谓业务逻辑代码,其实业务逻辑可以分离出两部分:做什么(业务工作流)和怎么做(具体实现),
Service只实现做什么,怎么做是领域对象Domain的事,这就是Domain 的行为。
贫血模型
Domain中出现了【不依赖于持久化】的逻辑,分离了做什么、怎么做, 做什么就会放到Service层,怎么做放Domain。
用户领域对象
@Data
public class UserInfoDomain {
private String name;
private int age;
private String IDCard;
private Date birthday;
private String sex;
private String status;
private List<String> authoritys;
//身份证号合法性校验
public boolean idCardCheck(){
......
}
//注册校验
public boolean registCteck(){
if(this.age < 18 || this.age > 80){
return false;
}
if(this.sex.equals("F")){
return false;
}
return true;
}
//添加权限
public List<String> addAuth(){
//根据sex获取权限数据
return list;
}
}
@Component
public class UserInfoFactory implements BaseDomainFactory<UserIn foDomain>{
@Override
public UserInfoDomain compose(Request request) {
UserInfoDomain userInfoDomain = new UserInfoDomain();
String cardNo = request.getIDCard();//身份证号
Date birthday = DateUtil.toDate(cardNo.substring(6, 14));//生日
String sex = cardNo.substring(cardNo.length() ‐2 , cardNo.l ength() ‐1);//性别
int age = DateUtil.getAge(birthday);//年龄
userInfoDomain.setBirthday(birthday);
userInfoDomain.setSex(sex == 1 ? "M" : "F";); userInfoDomain.setAge(age); userInfoDomain.setStatus(INIT);
return userInfoDomain;
}
}
业务逻辑层代码
@Service
public class UserInfoService {
@Autowired
private UserInfoRepository userInfoRepository;
public UserInfoDomain loadUserById(Long id) {
return userInfoRepository.loadUserById(id);
}
public void addUser(UserInfoDomain userInfoDomain) {
if (null != userInfoRepository.get(userInfoDomain.getIDCard())) {
throw new BusinessException(BizCode.IDCard_IS_EXIST);
}
//身份证号合法性校验
if(!userInfoDomain.idCardCheck()){
throw new BusinessException(BizCode.IDCard_ILLEGAL);
}
//注册限制校验
if(!userInfoDomain.registCteck()){
throw new BusinessException(BizCode.NOT_QUALIFIED);
}
//添加用户
userInfoRepository.add(userInfoDomain);
//添加权限
List<String> authList = userInfoDomain.addAuth();
userAuthRepository.add(list);
}
}
idCardCheck、registCteck逻辑是属于UserInfoDomain的行为放在Domain中。UserInfoDomain、authList只是在UserInfoDomain中生成而已,并没有做持久化操作。
调用路径为Controller-->Service-->Domain/Repository-->Mapper
充血模型
同贫血模型的区别是:持久化是Domain行为的一部分,Service是很薄的一层,仅仅封装事务和少量逻辑,不和仓库层打交道。
用户领域对象
@Data
public class UserInfoDomain {
@Autowired
private UserInfoRepository userInfoRepository;
@Autowired
private UserAuthRepository userAuthRepository;
private String name;
private int age;
private String IDCard;
private Date birthday;
private String sex;
private String status;
private List<String> authoritys;
//身份证号合法性校验
public boolean idCardCheck(){
......
}
//注册校验
public boolean registCteck(){
if(this.age < 18 || this.age > 80){
return false;
}
if(this.sex.equals("F")){
return false;
}
return true;
}
//添加权限
public void addAuth(){
//根据sex获取权限数据
userAuthRepository.add(list);
}
//添加用户
public void addUser(){
userInfoRepository.add(this);
}
}
业务逻辑代码
@Service
public class UserInfoService {
@Autowired
private UserInfoRepository userInfoRepository;
public UserInfoDomain loadUserById(Long id) {
return userInfoRepository.loadUserById(id);
}
public void addUser(UserInfoDomain userInfoDomain) {
if (null != userInfoRepository.get(userInfoDomain.getIDCard())) {
throw new BusinessException(BizCode.IDCard_IS_EXIST);
}
//身份证号合法性校验
if(!userInfoDomain.idCardCheck()){
throw new BusinessException(BizCode.IDCard_ILLEGAL);
}
//注册限制校验
if(!userInfoDomain.registCteck()){
throw new BusinessException(BizCode.NOT_QUALIFIED);
}
//添加用户
userInfoDomain.addUser();
//添加权限
userInfoDomain.addAuth();
}
}
调用路径为Controller-->Service-->Domain-->Repository-->Mapper 至此,Service只是做业务节点的编排,将对于数据库的操作变成了Domain行为的一部分了。
胀血模型
基于充血模型,可以认为Service已经毫无作用了,调用路径可以简化为Controller-->Domain-->Repository-->Mapper。
那就考虑一个问题,是否所有的业务逻辑都是Domain的行为?如果不是,那这些逻辑放哪里?
总结
我们用哪种?
失血模型和胀血模型一般是不推荐的,但是有一条是确定的:Service 只是业务的协调者,并不是业务的实施者,Domain才是业务实施者。 例如:餐厅中的服务员只是顾客和厨房之间的业务协调,服务员是知道做什么的,服务员并不能决定菜怎么做。