Java開發架構篇:DDD模型領域層決策規則樹服務設計

小傅哥發表於2020-05-20

作者:小傅哥
部落格:https://bugstack.cn

沉澱、分享、成長,讓自己和他人都能有所收穫!

一、前言

在上一章節介紹了領域驅動設計的基本概念以及按照領域驅動設計的思想進行程式碼分層,但是僅僅只是從一個簡單的分層結構上依然沒法理解DDD以及如何去開發這樣的微服務。另外往往按照這樣分層後依然感覺和MVC也沒有什麼差別,也沒有感受到帶來什麼非常大的好處。那麼問題出在哪呢?我個人覺得DDD學起來更像是一套指導思想,不斷的將學習者引入到領域觸發的思維中去,而這恰恰也是最難學習的地方。時而感覺會了,而實際開發中又不對,本來已經拆解清晰了,但怎麼又那麼像MVC了。甚至懷疑自己,我在幹嘛?

無論是DDD、MVC,他們更像是家裡三居或者四局的格局,每一種格局方式都是為了更好的實現對應架構下的設計思想。但,不是說給你一個通用的架構模式,你就能開發出乾淨(高內聚)、整潔(低耦合)、漂亮(模組化)的程式碼。這就像是你家住三居、他家也住三居,但是你們屋子的舒適情況就一樣嗎?{再有,你家裡會把廁所安在廚房嗎?但你的程式碼是否這麼幹過,不合理的擺放導致重構延期。}

另外DDD之所以看著簡單但又不那麼好落地,個人認為很重要就是領域思想,DDD只是指導但是不能把網際網路天下每一個業務行為開發都拿出來舉例子給你看,每個領域需要設計。所以需要一些領域專家{產品+架構+不是槓精的程式猿}來討論梳理,將業務形態設計出合理的架構&程式碼。

二、案例目標

本案例通過一個商品下單規則的場景來進行演示DDD;

  1. 假設產品需求業務執行人員可以對不同的商品配置一些規則,這些規則可以滿足不同使用者型別可以下單不同商品。
  2. 另外一些行為規則是會隨著業務發展而增加或者變動的,所以不能寫死{寫死太嚇人了}。
  3. 資料庫的PO類不應該被外部服務呼叫,這也是必須的。如果你開發過很多系統,那麼可能已經吃過虧並意識到這個問題。
  4. 按照DDD思想我們嘗試需要設計一個規則引擎的服務,通過給外部提供非常簡單的介面(application)來獲取最終結果。
  5. 通過這樣的案例可以很容易的感受到目前的四層架構確實在實現DDD思想上有很多的幫助。

如圖;DDD分層結構 | 指導設計架構

bugstack蟲洞棧 | 分層結構

三、DDD思想 · 開發設計

通過領域驅動設計的思想,從領域知識中提取和劃分為一個一個的子領域(核心子域,通用子域,支撐子域),並在子領域上建立模型。那麼在技術實現上就需要去支撐這種建模,以使我們的程式碼模組獨立、免汙染、易於擴充套件。

在上面我們提到需要開發一個可擴充套件使用的規則樹,那麼如果只是單純的一次性需求,最快的方式是if語句就搞定了。但是為了使這個領域服務具備良好的使用和擴充套件性,我們需要做些拆分,那麼如下;
1、你是否想過系統在過濾過則的時候其實就像執行一棵二叉樹一樣非左即右側,每一條線上都有著執行條件,通過判斷來達到最終的結果。
2、按照樹形結構我們將定義出來四個類;樹、節點、果實、指向線(From-To),用於描述我們的規則行為。
3、再此基礎上需要實現一個邏輯定義與規則樹執行引擎,通過統一的引擎服務來執行我們每次配置好的規則樹。

如圖;領域開發設計服務

微信公眾號:bugstack蟲洞棧 | 領域開發設計服務

四、工程模型

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 | 應用層定義介面服務,也可以適當做簡單包裝

/**
 * 商超規則過濾服務;提供規則樹決策功能
 * 微信公眾號: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來實現應用層介面

/**
 * 規則樹服務;提供規則規律功能
 *
 * 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 | 邏輯決策定義

/**
 * 微信公眾號:bugstack蟲洞棧 | 專注原創技術專題案例
 * 論壇:http://bugstack.cn
 * Create by 付政委 on @2019
 */
public interface LogicFilter {

    /**
     * 邏輯決策器
     * @param matterValue          決策值
     * @param treeNodeLineInfoList 決策節點
     * @return                     下一個節點Id
     */
    Long filter(String matterValue, List<TreeNodeLineInfo> treeNodeLineInfoList);

    /**
     * 獲取決策值
     *
     * @param decisionMatter 決策物料
     * @return               決策值
     */
    String matterValue(DecisionMatter decisionMatter);

}

domain/service/engine/EngineFilter.java | 引擎執行定義

/**
 * 微信公眾號:bugstack蟲洞棧 | 專注原創技術專題案例
 * 論壇:http://bugstack.cn
 * Create by 小傅哥 on @2019
 */
public interface EngineFilter {

    EngineResult process(final DecisionMatter matter) throws Exception;

}

infrastructure基礎層

  1. 實現領域層倉儲定義
  2. 資料庫操作為非業務屬性的功能操作
  3. 在倉儲實現層進行組合裝配DAO&Redis&Cache等

infrastructure/repository/RuleRepository.java

/**
 * 微信公眾號:bugstack蟲洞棧 | 專注原創技術專題案例
 * 論壇:http://bugstack.cn
 * Create by 小傅哥 on @2019
 */
public interface EngineFilter {

    EngineResult process(final DecisionMatter matter) throws Exception;

}

interfaces介面層

  1. 包裝應用介面對外提供api
  2. 外部傳輸物件採用DTO類,主要為了避免內部類被汙染{不斷的迭代的需求會在類中增加很多欄位}
  3. 目前依然是提供的Http服務,如果提供的rpc服務,將需要對外提供可引用jar

interfaces/DDDController.java

**
 * 微信公眾號:bugstack蟲洞棧 | 歡迎關注學習專題案例
 * 論壇:http://bugstack.cn
 * Create by 小傅哥 on @2019
 */
@Controller
public class DDDController {

    private Logger logger = LoggerFactory.getLogger(DDDController.class);

    @Resource
    private MallTreeService mallTreeService;
    @Resource
    private MallRuleService mallRuleService;

    /**
     * 測試介面:http://localhost:8080/api/tree/queryTreeSummaryInfo
     * 請求引數:{"treeId":10001}
     */
    @RequestMapping(path = "/api/tree/queryTreeSummaryInfo", method = RequestMethod.POST)
    @ResponseBody
    public ResponseEntity queryTreeSummaryInfo(@RequestBody TreeDTO request) {
        String reqStr = JSON.toJSONString(request);
        try {
            logger.info("查詢規則樹資訊{}Begin req:{}", request.getTreeId(), reqStr);
            TreeCollect treeCollect = mallTreeService.queryTreeSummaryInfo(request.getTreeId());
            logger.info("查詢規則樹資訊{}End res:{}", request.getTreeId(), JSON.toJSON(treeCollect));
            return new ResponseEntity<>(treeCollect, HttpStatus.OK);
        } catch (Exception e) {
            logger.error("查詢規則樹資訊{}Error req:{}", request.getTreeId(), reqStr, e);
            return new ResponseEntity<>(e.getMessage(), HttpStatus.OK);
        }
    }

    /**
     * 測試介面:http://localhost:8080/api/tree/decisionRuleTree
     * 請求引數:{"treeId":10001,"userId":"fuzhengwei","valMap":{"gender":"man","age":"25"}}
     */
    @RequestMapping(path = "/api/tree/decisionRuleTree", method = RequestMethod.POST)
    @ResponseBody
    public ResponseEntity decisionRuleTree(@RequestBody DecisionMatterDTO request) {
        String reqStr = JSON.toJSONString(request);
        try {
            logger.info("規則樹行為資訊決策{}Begin req:{}", request.getTreeId(), reqStr);
            DecisionMatter decisionMatter = new DecisionMatter();
            decisionMatter.setTreeId(request.getTreeId());
            decisionMatter.setUserId(request.getUserId());
            decisionMatter.setValMap(request.getValMap());
            EngineResult engineResult = mallRuleService.process(decisionMatter);
            logger.info("規則樹行為資訊決策{}End res:{}", request.getTreeId(), JSON.toJSON(engineResult));
            return new ResponseEntity<>(engineResult, HttpStatus.OK);
        } catch (Exception e) {
            logger.error("規則樹行為資訊決策{}Error req:{}", request.getTreeId(), reqStr, e);
            return new ResponseEntity<>(e.getMessage(), HttpStatus.OK);
        }
    }

}

五、測試驗證

規則樹結構{資料庫轉Json} | 可自行定義

{
    "treeNodeMap": {
        "1": {
            "nodeType": 1,
            "ruleDesc": "使用者性別[男/女]",
            "ruleKey": "userGender",
            "treeId": 10001,
            "treeNodeId": 1,
            "treeNodeLineInfoList": [
                {
                    "nodeIdFrom": 1,
                    "nodeIdTo": 11,
                    "ruleLimitType": 1,
                    "ruleLimitValue": "man"
                },
                {
                    "nodeIdFrom": 1,
                    "nodeIdTo": 12,
                    "ruleLimitType": 1,
                    "ruleLimitValue": "woman"
                }
            ]
        },
        "11": {
            "nodeType": 1,
            "ruleDesc": "使用者年齡",
            "ruleKey": "userAge",
            "treeId": 10001,
            "treeNodeId": 11,
            "treeNodeLineInfoList": [
                {
                    "nodeIdFrom": 11,
                    "nodeIdTo": 111,
                    "ruleLimitType": 3,
                    "ruleLimitValue": "25"
                },
                {
                    "nodeIdFrom": 11,
                    "nodeIdTo": 112,
                    "ruleLimitType": 3,
                    "ruleLimitValue": "25"
                }
            ]
        },
        "12": {
            "nodeType": 1,
            "ruleDesc": "使用者年齡",
            "ruleKey": "userAge",
            "treeId": 10001,
            "treeNodeId": 12,
            "treeNodeLineInfoList": [
                {
                    "nodeIdFrom": 12,
                    "nodeIdTo": 121,
                    "ruleLimitType": 3,
                    "ruleLimitValue": "25"
                },
                {
                    "nodeIdFrom": 12,
                    "nodeIdTo": 122,
                    "ruleLimitType": 3,
                    "ruleLimitValue": "25"
                }
            ]
        },
        "111": {
            "nodeType": 2,
            "nodeValue": "果實A",
            "treeId": 10001,
            "treeNodeId": 111,
            "treeNodeLineInfoList": [ ]
        },
        "112": {
            "nodeType": 2,
            "nodeValue": "果實B",
            "treeId": 10001,
            "treeNodeId": 112,
            "treeNodeLineInfoList": [ ]
        },
        "121": {
            "nodeType": 2,
            "nodeValue": "果實C",
            "treeId": 10001,
            "treeNodeId": 121,
            "treeNodeLineInfoList": [ ]
        },
        "122": {
            "nodeType": 2,
            "nodeValue": "果實D",
            "treeId": 10001,
            "treeNodeId": 122,
            "treeNodeLineInfoList": [ ]
        }
    },
    "treeRoot": {
        "treeId": 10001,
        "treeName": "購物分類規則樹",
        "treeRootNodeId": 1
    }
}

通過postman呼叫 | raw => json

查詢規則樹資訊
測試介面:http://localhost:8080/api/tree/decisionRuleTree 請求引數:{"treeId":10001}`

{
    "treeInfo": {
        "treeId": 10001,
        "treeName": "購物分類規則樹",
        "treeDesc": "用於分類不同型別使用者可購物範圍",
        "nodeCount": 7,
        "lineCount": 6
    },
    "treeRulePointList": [
        {
            "ruleKey": "userGender",
            "ruleDesc": "使用者性別[男/女]"
        },
        {
            "ruleKey": "userAge",
            "ruleDesc": "使用者年齡"
        }
    ]
}

微信公眾號:bugstack蟲洞棧 | 查詢規則樹資訊

規則樹行為資訊決策

測試介面:http://localhost:8080/api/tree/decisionRuleTree
請求引數:{"treeId":10001}

{
    "userId": "fuzhengwei",
    "treeId": 10001,
    "nodeId": 112,
    "nodeValue": "果實B",
    "success": true
}

微信公眾號:bugstack蟲洞棧 | 規則樹行為資訊決策

服務端

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v2.0.5.RELEASE)

2019-10-19 18:22:05.672  INFO 13820 --- [           main] org.itstack.demo.DDDApplication          : Starting DDDApplication on fuzhengwei-PC with PID 13820 (E:\itstack\itstack.org\itstack-demo-ddd-02\target\classes started by fuzhengwei in E:\itstack\itstack.org\itstack-demo-ddd-02)
2019-10-19 18:22:05.675  INFO 13820 --- [           main] org.itstack.demo.DDDApplication          : No active profile set, falling back to default profiles: default
2019-10-19 18:22:05.952  INFO 13820 --- [           main] ConfigServletWebServerApplicationContext : Refreshing org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@3c4297f: startup date [Sat Oct 19 18:22:05 CST 2019]; root of context hierarchy
2019-10-19 18:22:07.756  INFO 13820 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8080 (http)
2019-10-19 18:22:07.870  INFO 13820 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2019-10-19 18:22:07.870  INFO 13820 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet Engine: Apache Tomcat/8.5.34
2019-10-19 18:22:07.896  INFO 13820 --- [ost-startStop-1] o.a.catalina.core.AprLifecycleListener   : The APR based Apache Tomcat Native library which allows optimal performance in production environments was not found on the java.library.path: [D:\Program Files Java\Java\jdk1.8.0_162\bin;C:\Windows\Sun\Java\bin;C:\Windows\system32;C:\Windows;C:\ProgramData\Oracle\Java\javapath;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Windows\System32\WindowsPowerShell\v1.0\;D:\Program Files Java\SlikSvn\bin;D:\Program Files Java\MySQL Server 5.1\bin;D:\Program Files Java\TortoiseGit\bin;D:\Program Files\nodejs\;D:\Program Files Java\Java\jdk1.6.0_24\bin;D:\Program Files Java\apache-maven-3.2.3\bin;C:\Users\fuzhengwei\AppData\Roaming\npm;D:\Program Files Java\Git\cmd;;.]
2019-10-19 18:22:08.040  INFO 13820 --- [ost-startStop-1] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2019-10-19 18:22:08.040  INFO 13820 --- [ost-startStop-1] o.s.web.context.ContextLoader            : Root WebApplicationContext: initialization completed in 2088 ms
2019-10-19 18:22:08.102  INFO 13820 --- [ost-startStop-1] o.s.b.w.servlet.ServletRegistrationBean  : Servlet dispatcherServlet mapped to [/]
2019-10-19 18:22:08.126  INFO 13820 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean   : Mapping filter: 'characterEncodingFilter' to: [/*]
2019-10-19 18:22:08.127  INFO 13820 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean   : Mapping filter: 'hiddenHttpMethodFilter' to: [/*]
2019-10-19 18:22:08.127  INFO 13820 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean   : Mapping filter: 'httpPutFormContentFilter' to: [/*]
2019-10-19 18:22:08.127  INFO 13820 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean   : Mapping filter: 'requestContextFilter' to: [/*]
2019-10-19 18:22:09.118  INFO 13820 --- [           main] o.s.w.s.handler.SimpleUrlHandlerMapping  : Mapped URL path [/**/favicon.ico] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2019-10-19 18:22:09.383  INFO 13820 --- [           main] s.w.s.m.m.a.RequestMappingHandlerAdapter : Looking for @ControllerAdvice: org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@3c4297f: startup date [Sat Oct 19 18:22:05 CST 2019]; root of context hierarchy
2019-10-19 18:22:10.261  INFO 13820 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/api/tree/decisionRuleTree],methods=[POST]}" onto public org.springframework.http.ResponseEntity org.itstack.demo.interfaces.DDDController.decisionRuleTree(org.itstack.demo.interfaces.dto.DecisionMatterDTO)
2019-10-19 18:22:10.263  INFO 13820 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/api/tree/queryTreeSummaryInfo],methods=[POST]}" onto public org.springframework.http.ResponseEntity org.itstack.demo.interfaces.DDDController.queryTreeSummaryInfo(org.itstack.demo.interfaces.dto.TreeDTO)
2019-10-19 18:22:10.272  INFO 13820 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error]}" onto public org.springframework.http.ResponseEntity<java.util.Map<java.lang.String, java.lang.Object>> org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController.error(javax.servlet.http.HttpServletRequest)
2019-10-19 18:22:10.274  INFO 13820 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error],produces=[text/html]}" onto public org.springframework.web.servlet.ModelAndView org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController.errorHtml(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse)
2019-10-19 18:22:10.309  INFO 13820 --- [           main] o.s.w.s.handler.SimpleUrlHandlerMapping  : Mapped URL path [/webjars/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2019-10-19 18:22:10.309  INFO 13820 --- [           main] o.s.w.s.handler.SimpleUrlHandlerMapping  : Mapped URL path [/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2019-10-19 18:22:16.272  INFO 13820 --- [           main] o.s.j.e.a.AnnotationMBeanExporter        : Registering beans for JMX exposure on startup
2019-10-19 18:22:16.273  INFO 13820 --- [           main] o.s.j.e.a.AnnotationMBeanExporter        : Bean with name 'dataSource' has been autodetected for JMX exposure
2019-10-19 18:22:16.279  INFO 13820 --- [           main] o.s.j.e.a.AnnotationMBeanExporter        : Located MBean 'dataSource': registering with JMX server as MBean [com.zaxxer.hikari:name=dataSource,type=HikariDataSource]
2019-10-19 18:22:16.375  INFO 13820 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8080 (http) with context path ''
2019-10-19 18:22:16.381  INFO 13820 --- [           main] org.itstack.demo.DDDApplication          : Started DDDApplication in 11.458 seconds (JVM running for 20.584)
2019-10-19 18:22:31.336  INFO 13820 --- [nio-8080-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring FrameworkServlet 'dispatcherServlet'
2019-10-19 18:22:31.336  INFO 13820 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : FrameworkServlet 'dispatcherServlet': initialization started
2019-10-19 18:22:31.372  INFO 13820 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : FrameworkServlet 'dispatcherServlet': initialization completed in 36 ms
2019-10-19 18:22:32.427  INFO 13820 --- [nio-8080-exec-1] o.itstack.demo.interfaces.DDDController  : 規則樹行為資訊決策10001Begin req:{"treeId":10001,"userId":"fuzhengwei","valMap":{"gender":"man","age":"25"}}
2019-10-19 18:22:32.508  INFO 13820 --- [nio-8080-exec-1] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Starting...
2019-10-19 18:22:32.956  INFO 13820 --- [nio-8080-exec-1] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Start completed.
2019-10-19 18:22:33.028  INFO 13820 --- [nio-8080-exec-1] o.i.d.d.rule.service.engine.EngineBase   : 樹引擎=>Test分類規則樹 userId:fuzhengwei treeId:10001 treeNode:11 ruleKey:userGender matterValue:man
2019-10-19 18:22:33.028  INFO 13820 --- [nio-8080-exec-1] o.i.d.d.rule.service.engine.EngineBase   : 樹引擎=>Test分類規則樹 userId:fuzhengwei treeId:10001 treeNode:112 ruleKey:userAge matterValue:25
2019-10-19 18:22:33.039  INFO 13820 --- [nio-8080-exec-1] o.itstack.demo.interfaces.DDDController  : 規則樹行為資訊決策10001End res:{"treeId":10001,"nodeValue":"果實B","success":true,"nodeId":112,"userId":"fuzhengwei"}
2019-10-19 18:23:36.989  INFO 13820 --- [nio-8080-exec-5] o.itstack.demo.interfaces.DDDController  : 查詢規則樹資訊10001Begin req:{"treeId":10001}
2019-10-19 18:23:37.006  INFO 13820 --- [nio-8080-exec-5] o.itstack.demo.interfaces.DDDController  : 查詢規則樹資訊10001End res:{"treeInfo":{"treeId":10001,"treeName":"購物分類規則樹","treeDesc":"用於分類不同型別使用者可購物範圍","nodeCount":7,"lineCount":6},"treeRulePointList":[{"ruleDesc":"使用者性別[男/女]","ruleKey":"userGender"},{"ruleDesc":"使用者年齡","ruleKey":"userAge"}]}

六、綜上總結

  • 以上模擬購物場景下的規則處理抽象為樹決策引擎,以達到獨立領域服務。另外決策服務可以使用drools,任何抽象並不一定永遠使用,不要拘泥於一種形式
  • 一些大型架構設計往往不是換一個設計模型就能徹底提升效率,還是需要人員整體素質,這是一個不斷培養的過程
  • 領域驅動設計的思想並不只是教會程式猿寫程式碼,也是非程式設計師以外的所有網際網路人員都適合學習的內容
  • 家裡住的舒適不舒適,並不一定取決於三居或者四居,大部分還是依賴於怎麼對格局的佈置。事必躬親、親力親為的精益求精之路,終究會讓你設計出更加合理的程式碼

七、推薦閱讀

相關文章