該文章主要介紹自己根據對領域驅動設計的理解結合開發實際而賦予領域對象更多的實際行爲,而不僅僅是作爲一個簡單的數據載體而存在。
DO(Domain Object)優化
在貧血模型裏,領域對象一般只作爲數據載體而存在,只有get和set方法,這也導致一些簡單的業務邏輯重複出現在多個地方,如僅把對象裏的值提出來去處理判斷,但這些行爲放到對象中可以更加豐富DO的行爲,增加代碼可讀性,下面以一個優化對象作爲數據載體與賦予簡單邏輯後的操作對比爲例。
@Data
@Accessors(chain = true)
public class Discount {
private Long id;
/**
* 狀態(0-禁用,1-可用,2-過期) {@link DiscountConstant.Status}
*/
private Integer status;
/**
* 類型(1-折扣,2-優惠券) {@link DiscountConstant.Type}
*/
private Integer type;
/**
* 折扣/優惠券價
*/
private Integer value;
}
當需要進行一些邏輯處理,如返回優惠信息,判斷狀態等,可能就會在多處出現以下的代碼塊:
if(Objects.equals(discount.getStatus(),status){
// do something
}
String discountMsg = DiscountConstant.Type.DISCOUNT.equals(discount.getType()) ? discount.getValue() + "折" : "優惠券" + discount.getValue() + "元";
@Data
@Accessors(chain = true)
public class Discount {
private Long id;
/**
* 狀態(0-禁用,1-可用,2-過期) {@link DiscountConstant.Status}
*/
private Integer status;
/**
* 類型(1-折扣,2-優惠券) {@link DiscountConstant.Type}
*/
private Integer type;
/**
* 折扣/優惠券價
*/
private Integer value;
/**
* {@link DiscountConstant.Status}
*/
public boolean isStatusEqual(Integer status) {
return Objects.equals(this.status, status);
}
/**
* {@link DiscountConstant.Type}
*/
public boolean isTypeEquals(Integer type) {
return Objects.equals(this.type, type);
}
/**
* {@link DiscountConstant.Type}
*/
public String getDiscountMsg() {
return DiscountConstant.Type.DISCOUNT.equals(this.type) ? value + "折" : "優惠券" + value + "元";
}
}
當要獲取優惠信息時,如果返回頁面的是Discount這個對象時,則無需任何處理了,getDiscountMsg方法會返回discountMsg信息;當作爲邏輯處理獲取信息時,也可以調用discountMsg而無需重複寫類型判斷與字符串拼接。進行狀態判斷時,使用領域對象裏的方法進行判斷更具有可讀性:
if(discount.isStatusEquals(status)){
// do something
}
VO(Value Object)優化
值對象在Web應用層中一般表示爲表現層(前端)的對象,與DDD中的值對象有所不同。前端進行VO傳參時,VO也可進行類似DO的優化,如複雜參數的校驗,對象的轉換,對象轉換例子:
@Data
public class UserVO {
@NotBlank
private String id;
@NotBlank
private String username;
@NotBlank
private String password;
@Range(min = 1, max = 3)
private Integer status;
public UserBase userBase(){
UserBase userBase = new UserBase();
BeanUtils.copyProperties(this,userBase);
return userBase
}
}
引申領域模型
- 貧血模型:領域對象(DO)裏沒有業務邏輯,只含簡單而的數據操作方法(如get、set、toString),從業務層面上看僅僅是一個數據載體在業務與存儲介質之間進行傳輸的數據載體。
- 充血模型:領域對象裏擁有自己的狀態與行爲,大多業務邏輯和持久化放在DO裏,業務邏輯層只是簡單封裝部分業務邏輯以及控制事務等。
貧血模型與充血模型應用分層差別不大,主要區別在於邏輯的處理,如下圖(左圖參考自《領域驅動設計:軟件核心複雜性應對之道》,右圖參考自《阿里巴巴Java開發手冊》):
結合實際
充血模型對開發人員的業務瞭解要求較高,需要開發人員清楚的認知業務劃分,什麼需要放到service層,什麼需要放到DO層,且DO中還包含DAO進行數據持久化(依賴倒置原則),這對開發人員來說十分混亂。但在實際開發中,並不一定必須按照死規則去編寫代碼,可以借鑑充血模型的邏輯處理,靈活開發,爲貧血模型中的領域對象賦予數據對象一定的邏輯,提高代碼的複用性、可讀性。