Java開發架構篇:DDD模型領域層決策規則樹服務設計
阿新 • • 發佈:2020-05-20
作者:小傅哥
部落格:[https://bugstack.cn](https://bugstack.cn)
>沉澱、分享、成長,讓自己和他人都能有所收穫!
## 一、前言
在上一章節介紹了領域驅動設計的基本概念以及按照領域驅動設計的思想進行程式碼分層,但是僅僅只是從一個簡單的分層結構上依然沒法理解DDD以及如何去開發這樣的微服務。另外往往按照這樣分層後依然感覺和MVC也沒有什麼差別,也沒有感受到帶來什麼非常大的好處。那麼問題出在哪呢?我個人覺得DDD學起來更像是一套指導思想,不斷的將學習者引入到領域觸發的思維中去,而這恰恰也是最難學習的地方。時而感覺會了,而實際開發中又不對,本來已經拆解清晰了,但怎麼又那麼像MVC了。甚至懷疑自己,我在幹嘛?
無論是DDD、MVC,他們更像是家裡三居或者四局的格局,每一種格局方式都是為了更好的實現對應架構下的設計思想。但,不是說給你一個通用的架構模式,你就能開發出乾淨(高內聚)、整潔(低耦合)、漂亮(模組化)的程式碼。這就像是你家住三居、他家也住三居,但是你們屋子的舒適情況就一樣嗎?{再有,你家裡會把廁所安在廚房嗎?但你的程式碼是否這麼幹過,不合理的擺放導致重構延期。}
另外DDD之所以看著簡單但又不那麼好落地,個人認為很重要就是領域思想,DDD只是指導但是不能把網際網路天下每一個業務行為開發都拿出來舉例子給你看,每個領域需要設計。所以需要一些領域專家{產品+架構+不是槓精的程式猿}來討論梳理,將業務形態設計出合理的架構&程式碼。
## 二、案例目標
**本案例通過一個商品下單規則的場景來進行演示DDD;**
1. 假設產品需求業務執行人員可以對不同的商品配置一些規則,這些規則可以滿足不同使用者型別可以下單不同商品。
2. 另外一些行為規則是會隨著業務發展而增加或者變動的,所以不能寫死{寫死太嚇人了}。
3. 資料庫的PO類不應該被外部服務呼叫,這也是必須的。如果你開發過很多系統,那麼可能已經吃過虧並意識到這個問題。
4. 按照DDD思想我們嘗試需要設計一個規則引擎的服務,通過給外部提供非常簡單的介面(application)來獲取最終結果。
5. 通過這樣的案例可以很容易的感受到目前的四層架構確實在實現DDD思想上有很多的幫助。
>如圖;DDD分層結構 | 指導設計架構
![bugstack蟲洞棧 | 分層結構](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9idWdzdGFjay5jbi93cC1jb250ZW50L3VwbG9hZHMvMjAxOS8xMC9EREQtMDEtMi5wbmc?x-oss-process=image/format,png)
## 三、DDD思想 · 開發設計
通過領域驅動設計的思想,從領域知識中提取和劃分為一個一個的子領域(核心子域,通用子域,支撐子域),並在子領域上建立模型。那麼在技術實現上就需要去支撐這種建模,以使我們的程式碼模組獨立、免汙染、易於擴充套件。
在上面我們提到需要開發一個可擴充套件使用的規則樹,那麼如果只是單純的一次性需求,最快的方式是if語句就搞定了。但是為了使這個領域服務具備良好的使用和擴充套件性,我們需要做些拆分,那麼如下;
1、你是否想過系統在過濾過則的時候其實就像執行一棵二叉樹一樣非左即右側,每一條線上都有著執行條件,通過判斷來達到最終的結果。
2、按照樹形結構我們將定義出來四個類;樹、節點、果實、指向線(From-To),用於描述我們的規則行為。
3、再此基礎上需要實現一個邏輯定義與規則樹執行引擎,通過統一的引擎服務來執行我們每次配置好的規則樹。
>如圖;領域開發設計服務
![微信公眾號:bugstack蟲洞棧 | 領域開發設計服務](https://img-blog.csdnimg.cn/20191019213416986.png)
## 四、工程模型
```java
itstack-demo-ddd-02
└── src
├── main
│ ├── java
│ │ └── org.itstack.demo
│ │ ├── application
│ │ │ ├── MallRuleService.java
│ │ │ └── MallTreeService.java
│ │ ├── domain
│ │ │ ├── rule
│ │ │ │ ├── model
│ │ │ │ │ ├── aggregates
│ │ │ │ │ │ └── UserRichInfo.java
│ │ │ │ │ └── vo
│ │ │ │ │ ├── DecisionMatter.java
│ │ │ │ │ ├── EngineResult.java
│ │ │ │ │ ├── TreeNodeInfo.java
│ │ │ │ │ ├── TreeNodeLineInfo.java
│ │ │ │ │ └── UserSchool.java
│ │ │ │ ├── repository
│ │ │ │ │ └── IRuleRepository.java
│ │ │ │ └── service
│ │ │ │ ├── engine
│ │ │ │ │ ├── impl
│ │ │ │ │ └── EngineFilter.java
│ │ │ │ ├── logic
│ │ │ │ │ ├── impl
│ │ │ │ │ └── LogicFilter.java
│ │ │ │ └── MallRuleServiceImpl.java
│ │ │ └── tree
│ │ │ ├── model
│ │ │ │ ├── aggregates
│ │ │ │ │ └── TreeCollect.java
│ │ │ │ └── vo
│ │ │ │ ├── TreeInfo.java
│ │ │ │ └── TreeRulePoint.java
│ │ │ ├── repository
│ │ │ │ └── ITreeRepository.java
│ │ │ └── service
│ │ │ └── MallTreeServiceImpl.java
│ │ ├── infrastructure
│ │ │ ├── common
│ │ │ │ └── Constants.java
│ │ │ ├── dao
│ │ │ │ ├── RuleTreeDao.java
│ │ │ │ ├── RuleTreeNodeDao.java
│ │ │ │ └── RuleTreeNodeLineDao.java
│ │ │ ├── po
│ │ │ │ ├── RuleTree.java
│ │ │ │ ├── RuleTreeConfig.java
│ │ │ │ ├── RuleTreeNode.java
│ │ │ │ └── RuleTreeNodeLine.java
│ │ │ ├── repository
│ │ │ │ ├── cache
│ │ │ │ │ └── RuleCacheRepository.java
│ │ │ │ ├── mysql
│ │ │ │ │ ├── RuleMysqlRepository.java
│ │ │ │ │ └── TreeMysqlRepository.java
│ │ │ │ ├── RuleRepository.java
│ │ │ │ └── TreeRepository.java
│ │ │ └── util
│ │ │ └── CacheUtil.java
│ │ ├── interfaces
│ │ │ ├── dto
│ │ │ │ ├── DecisionMatterDTO.java
│ │ │ │ └── TreeDTO.java
│ │ │ └── DDDController.java
│ │ └── DDDApplication.java
│ └── resources
│ ├── mybatis
│ └── application.yml
└── test
└── java
└── org.itstack.demo.test
└── ApiTest.java
```
**演示部分重點程式碼塊,完整程式碼下載關注公眾號;bugstack蟲洞棧 | 回覆DDD落地**
### application應用層
>application/MallRuleService.java | 應用層定義介面服務,也可以適當做簡單包裝
```java
/**
* 商超規則過濾服務;提供規則樹決策功能
* 微信公眾號:bugstack蟲洞棧 | 專注原創技術專題案例
* 論壇:http://bugstack.cn
* Create by 小傅哥 on @2019
*/
public interface MallRuleService {
/**
* 決策服務
* @param matter 決策物料
* @return 決策結果
*/
EngineResult process(final DecisionMatter matter);
}
```
### domain領域層
domain中有兩個領域服務;規則樹資訊領域、規則執行領域,通過合理的抽象化來實現高內聚、低耦合的模組化服務
>domain/service/MallRuleServiceImpl.java | 領域層中的service來實現應用層介面
```java
/**
* 規則樹服務;提供規則規律功能
*
* 1、rule包下只進行規則決策領域的處理
* 2、封裝決策行為到領域模型中,外部只需要呼叫和處理結果即可
* 3、可以擴充套件不同的決策引擎進行統一管理
*
* 微信公眾號:bugstack蟲洞棧 | 專注原創技術專題案例
* 論壇:http://bugstack.cn
* Create by 小傅哥 on @2019
*/
@Service("mallRuleService")
public class MallRuleServiceImpl implements MallRuleService {
private Logger logger = LoggerFactory.getLogger(MallRuleServiceImpl.class);
@Resource(name = "ruleEngineHandle")
private EngineFilter ruleEngineHandle;
@Override
public EngineResult process(DecisionMatter matter) {
try {
return ruleEngineHandle.process(matter);
} catch (Exception e) {
logger.error("決策引擎執行失敗", e);
return new EngineResult(false);
}
}
}
```
>domain/service/logic/LogicFilter.java | 邏輯決策定義
```java
/**
* 微信公眾號:bugstack蟲洞棧 | 專注原創技術專題案例
* 論壇:http://bugstack.cn
* Create by 付政委 on @2019
*/
public interface LogicFilter {
/**
* 邏輯決策器
* @param matterValue 決策值
* @param treeNodeLineInfoList 決策節點
* @return 下一個節點Id
*/
Long filter(String matterVal