-
《專案實戰(影片)》:從書中學,往事上“練” -
《網際網路高頻面試題》:面朝簡歷學習,春暖花開 -
《架構 x 系統設計》:摧枯拉朽,掌控面試高頻場景題 -
《精進 Java 學習指南》:系統學習,網際網路主流技術棧 -
《必讀 Java 原始碼專欄》:知其然,知其所以然

-
Boot 倉庫:https://gitee.com/zhijiantianya/ruoyi-vue-pro -
Cloud 倉庫:https://gitee.com/zhijiantianya/yudao-cloud -
影片教程:https://doc.iocoder.cn
1、引言
-
專案地址:https://github.com/YunaiV/ruoyi-vue-pro -
影片教程:https://doc.iocoder.cn/video/
2、專案分層


2.1 domain層(核心)

-
事件:領域內部發生了某些事件,需要對相關的領域進行事件傳播,傳播的就是事件,比如一個下單動作,就會發送一個下單事件,告知倉儲領域鎖定庫存,所以需要定義一些事件物件。
-
實體和值物件:值物件: 值物件用於描述領域裡某個方面而本身沒有概念標識(無ID)的物件。值物件 不可變性:值物件一旦建立完成後就不可以修改;唯一方法是重新建立一個物件(所以值物件咱們不需要定義set方法) 可替換性:屬性相同的兩個值物件是完全等價的,在使用是可相互替換實體: 實體是重要的領域概念,實體的3個主要特徵:ID: 唯一的身份標識。ID相同代表實體是同一個 Lifecycle: 實體的生命週期具有連續性 Status: 生命期經歷不同的狀態(例如常見的訂單狀態)
-
聚合:一般由多個實體和值物件組成,具備行為
-
工廠:一個聚合體內部有多個實體和值物件這樣的話建立聚合體就需要一個工廠方法來簡化複雜的建立過程。
-
服務介面:領域內部一定有各種活動比如我們的訂單域,一定會有下單、結算、支付等服務介面,這些介面的實現也是在這一層實現的,但是domain層其實用不依賴其他任何層,所以這和個服務介面的實現都是無狀態的。
-
儲存介面:聚合體有行為,但是它對實體和值物件的修改需要具備持久化的能力,所以這裡需要定義資料持久化的部分介面,儲存聚合根的狀態。(這裡只定義介面實現交給基礎設施層來實現)
-
領域訪問介面:COLA架構推薦領域與領域之間的資料訪問應該解耦,需要使用閘道器來進行訪問

-
不依賴其他任何模組 -
這個模組可以有業務邏輯,但是這些業務邏輯是脫離基礎設施的,也就是說這些邏輯不依賴於你使用Mysql或者Oracle而改變邏輯實現,具體使用那種技術中介軟體,這是基礎設施層的職責。 -
這個模組的類不是都是貧血模型,是具有行為的。

2.2 infra層 (基礎設施層,和中介軟體打交道)
-
資料訪問層的全部程式碼,例如 mybatis的mapper介面,xml檔案 -
工具類 -
實現領域層定義的倉儲介面、服務介面、閘道器介面 -
模型轉換,MapStruts -
Spring Configuration配置類
2.3 app層 (業務層)
-
透過呼叫基礎設施層的能力或者領域層的介面完成Client層(如果有)定義的所有介面的程式碼實現 -
透過呼叫基礎設施層的能力或者領域層的介面完成Controller介面或者HSF介面的實際邏輯的實現
2.4 adapter層
-
Controller介面 -
統一異常處理類 -
引數檢驗(@Validated) -
controller的攔截器(Interceptor) 過濾器等 -
定時任務 -
業務自定義切面

2.5 client 層
-
RPC 請求的響應模型和請求模型即DTO -
RPC 請求的介面定義 -
部分服務消費者需要使用到的列舉 -
對專案服務消費者側的錯誤碼

2.6 start模組
-
突出顯示啟動類的位置 -
這個模組會依賴其他所有模組,所以很適合做maven打包入口 -
因為依賴了其他所有模組所以適合在此模組編寫單元測試程式碼 -
可以在這個啟動類上新增全域性配置 (類似:@EnableXXX @ComponentScan …)
@Data
publicclassWorkOrder
{
* 工單唯一id
*/
private
String workOrderId;
* 父工單id
*/
private
String parentOrderId;
* 發起人id
*/
private
String initiatorId;
private
Date startTime;
private
Date processTime;
private
Date completeTime;
private
Date rejectTime;
* 工單內容: 值物件:包含 圖片、工單型別、文字內容等
*/
private
WorkOrderContent content;
* 工單處理狀態: 值物件 列舉
*/
private
WorkOrderStatus workOrderStatus;
* 工單處理結果: 值物件,包含發起人,處理人,處理結果,處理意見等內容
*/
private
WorkOrderHandleResult workOrderHandleResult;
privateWorkOrder(Date startTime, String workOrderId, String initiatorId, WorkOrderContent content, WorkOrderStatus workOrderStatus, WorkOrderHandleResult workOrderHandleResult)
{
this
.workOrderId = workOrderId;
this
.initiatorId = initiatorId;
this
.content = content;
this
.workOrderStatus = workOrderStatus;
this
.workOrderHandleResult = workOrderHandleResult;
this
.startTime = startTime;
}
* 發起工單
*
*
@return
WorkOrder
*/
publicstatic WorkOrder start(String initiatorId, WorkOrderContent content)
{
DateTimeFormatter chargeSeqDateFormatter = DateTimeFormatter.ofPattern(
"yyMMddHHmmssSSS"
);
String wordOrderId =
"WorkOrderID"
+ chargeSeqDateFormatter.format(LocalDateTime.now())
+
new
DecimalFormat(
"000"
).format(
new
SecureRandom().nextInt(
999
));
WorkOrderStatus workOrderStatus = WorkOrderStatus.INITIAL;
returnnew
WorkOrder(
new
Date(), wordOrderId, initiatorId, content, workOrderStatus,
null
);
}
* 處理工單
*/
publicvoidhandle()
{
this
.workOrderStatus = WorkOrderStatus.HANDLEING;
this
.processTime =
new
Date();
}
* 處理完結
*
*
@param
workOrderHandleResult 工單處理結果
*/
publicvoidfinish(WorkOrderHandleResult workOrderHandleResult)
{
this
.completeTime =
new
Date();
this
.setWorkOrderHandleResult(workOrderHandleResult);
this
.workOrderStatus = WorkOrderStatus.COMPLETED;
}
* 工單駁回 這裡重新啟動一個工單
*/
public WorkOrder reject()
{
if
(!workOrderStatus.equals(WorkOrderStatus.COMPLETED)) {
returnnull
;
}
String parentWorkOrderId = workOrderId;
Date startTime = getStartTime();
DateTimeFormatter chargeSeqDateFormatter = DateTimeFormatter.ofPattern(
"yyMMddHHmmssSSS"
);
String newWorkOrderId =
"WorkOrderID"
+ chargeSeqDateFormatter.format(LocalDateTime.now())
+
new
DecimalFormat(
"000"
).format(
new
SecureRandom().nextInt(
999
));
WorkOrder workOrder =
new
WorkOrder(startTime, workOrderId, initiatorId, content, WorkOrderStatus.HANDLEING,
null
);
workOrder.setParentOrderId(parentOrderId);
workOrder.setRejectTime(
new
Date());
return
workOrder;
}
}
@Service
publicclassWorkOrderServiceImplimplementsWorkOrderService
{
@Autowired
WorkOrderRepository workOrderRepository;
@Autowired
WorkOrderEventRepository workOrderEventRepository;
@Override
public WorkOrder startOrder(String userId, WorkOrderContent workOrderContent)
{
WorkOrder order = WorkOrder.start(userId, workOrderContent);
workOrderRepository.save(order);
workOrderEventRepository.sendWorkOrderEvent(
new
WorkOrderEvent(order.getWorkOrderId(), order.getWorkOrderStatus().getValue()));
return
order;
}
@Override
publicvoidprocessOrder(WorkOrderHandleResult workOrderHandleResult, String handlerId, String workOrderId)
{
WorkOrder workOrder = workOrderRepository.get(workOrderId);
workOrder.handle();
workOrderEventRepository.sendWorkOrderEvent(
new
WorkOrderEvent(workOrder.getWorkOrderId(), workOrder.getWorkOrderStatus().getValue()));
workOrderRepository.save(workOrder);
}
}
publicinterfaceWorkOrderRepository
{
voidsave(WorkOrder workOrder)
;
WorkOrder get(String workOrderId)
;
}
publicinterfaceWorkOrderEventRepository
{
voidsendWorkOrderEvent(WorkOrderEvent workOrderEvent)
;
}
-
專案地址:https://github.com/YunaiV/yudao-cloud -
影片教程:https://doc.iocoder.cn/video/
總結





