封裝是面向對象的的第一大特性,屬性私有化,根據提供的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纔是業務實施者。 例如:餐廳中的服務員只是顧客和廚房之間的業務協調,服務員是知道做什麼的,服務員並不能決定菜怎麼做。