DDD专题案例二《领域层决策规则树服务设计》

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
云数据库 RDS MySQL,高可用系列 2核4GB
简介: 在上一章节介绍了领域驱动设计的基本概念以及按照领域驱动设计的思想进行代码分层,但仅仅只是从一个简单的分层结构上依然没法理解DDD以及如何去开发这样的微服务。另外往往按照这样分层后依然感觉和MVC也没有什么差别,也没有感受到带来什么非常大的好处。那么问题出在哪呢?我个人觉得DDD学起来更像是一套指导思想,不断的将学习者引入到领域触发的思维中去,而这恰恰也是最难学习的地方。时而感觉会了,而实际开发中又不对,本来已经拆解清晰了,但怎么又那么像MVC了。甚至怀疑自己,我在干嘛?

前言介绍

在上一章节介绍了领域驱动设计的基本概念以及按照领域驱动设计的思想进行代码分层,但仅仅只是从一个简单的分层结构上依然没法理解DDD以及如何去开发这样的微服务。另外往往按照这样分层后依然感觉和MVC也没有什么差别,也没有感受到带来什么非常大的好处。那么问题出在哪呢?我个人觉得DDD学起来更像是一套指导思想,不断的将学习者引入到领域触发的思维中去,而这恰恰也是最难学习的地方。时而感觉会了,而实际开发中又不对,本来已经拆解清晰了,但怎么又那么像MVC了。甚至怀疑自己,我在干嘛?

无论是DDD、MVC,他们更像是家里三居或者四局的格局,每一种格局方式都是为了更好的实现对应架构下的设计思想。,不是说给你一个通用的架构模式,你就能开发出干净(高内聚)、整洁(低耦合)、漂亮(模块化)的代码。这就像是你家住三居、他家也住三居,但是你们屋子的舒适情况就一样吗?{再有,你家里会把厕所安在厨房吗?但你的代码是否这么干过,不合理的摆放导致重构延期。}

另外DDD之所以看着简单但又不那么好落地,个人认为很重要就是领域思想,DDD只是指导但是不能把互联网天下每一个业务行为开发都拿出来举例子给你看每一个领域设计内容。所以在项目初期需要一些领域专家{产品+架构+不是杠精的程序猿}来讨论梳理,将业务形态设计出合理的架构&代码。

案例目标

本案例通过一个模拟商品下单规则的场景来进行演示DDD{这可能也是你平时的需求};

1、假设产品需求业务运行人员可以对不同的商品配置一些规则,这些规则可以满足不同用户类型可以下单不同商品。

2、另外一些行为规则是会随着业务发展而增加或者变动的,所以不能写死{写死太吓人了}。

3、数据库的PO类不应该被外部服务调用,这也是必须的。如果你开发过很多系统,那么可能已经吃过亏并意识到这个问题。

4、按照DDD思想我们尝试需要设计一个规则引擎的服务,通过给外部提供非常简单的接口(application)来获取最终结果。

5、通过这样的案例可以很容易的感受到目前的四层架构确实在实现DDD思想上有很多的帮助。

2.jpg

DDD思想 · 开发设计

通过领域驱动设计的思想,从领域知识中提取和划分为一个一个的子领域(核心子域,通用子域,支撑子域),并在子领域上建立模型。那么在技术实现上就需要去支撑这种建模,以使我们的代码模块独立、免污染、易于扩展。

在上面我们提到需要开发一个可扩展使用的规则树,那么如果只是单纯的一次性需求,最快的方式是if语句就搞定了。但是为了使这个领域服务具备良好的使用和扩展性,我们需要做些拆分,那么如下;

1、你是否想过系统在过滤过则的时候其实就像执行一棵二叉树一样非左即右侧,每一条线上都有着执行条件,通过判断来达到最终的结果。

2、按照树形结构我们将定义出来四个类;树、节点、果实、指向线(From-To),用于描述我们的规则行为。

3、再此基础上需要实现一个逻辑定义与规则树执行引擎,通过统一的引擎服务来执行我们每次配置好的规则树。

3.jpg

工程模型

1itstack-demo-ddd-02
 2└── src
 3    ├── main
 4    │   ├── java
 5    │   │   └── org.itstack.demo
 6    │   │       ├── application
 7    │   │       │    ├── MallRuleService.java    
 8    │   │       │    └── MallTreeService.java    
 9    │   │       ├── domain
10    │   │       │    ├── rule
11    │   │       │    │   ├── model
12    │   │       │    │   │   ├── aggregates
13    │   │       │    │   │   │   └── UserRichInfo.java   
14    │   │       │    │   │   └── vo
15    │   │       │    │   │       ├── DecisionMatter.java
16    │   │       │    │   │       ├── EngineResult.java
17    │   │       │    │   │       ├── TreeNodeInfo.java
18    │   │       │    │   │       ├── TreeNodeLineInfo.java   
19    │   │       │    │   │       └── UserSchool.java 
20    │   │       │    │   ├── repository
21    │   │       │    │   │   └── IRuleRepository.java    
22    │   │       │    │   └── service
23    │   │       │    │       ├── engine
24    │   │       │    │       │   ├── impl    
25    │   │       │    │       │   └── EngineFilter.java   
26    │   │       │    │       ├── logic
27    │   │       │    │       │   ├── impl    
28    │   │       │    │       │   └── LogicFilter.java    
29    │   │       │    │       └── MallRuleServiceImpl.java    
30    │   │       │    └── tree
31    │   │       │        ├── model
32    │   │       │        │   ├── aggregates
33    │   │       │        │   │   └── TreeCollect.java    
34    │   │       │        │   └── vo
35    │   │       │        │       ├── TreeInfo.java   
36    │   │       │        │       └── TreeRulePoint.java  
37    │   │       │        ├── repository
38    │   │       │        │   └── ITreeRepository.java    
39    │   │       │        └── service
40    │   │       │            └── MallTreeServiceImpl.java    
41    │   │       ├── infrastructure
42    │   │       │    ├── common
43    │   │       │    │   └── Constants.java
44    │   │       │    ├── dao
45    │   │       │    │   ├── RuleTreeDao.java
46    │   │       │    │   ├── RuleTreeNodeDao.java    
47    │   │       │    │   └── RuleTreeNodeLineDao.java    
48    │   │       │    ├── po
49    │   │       │    │   ├── RuleTree.java
50    │   │       │    │   ├── RuleTreeConfig.java
51    │   │       │    │   ├── RuleTreeNode.java   
52    │   │       │    │   └── RuleTreeNodeLine.java       
53    │   │       │    ├── repository
54    │   │       │    │   ├── cache
55    │   │       │    │   │   └── RuleCacheRepository.java
56    │   │       │    │   ├── mysql
57    │   │       │    │   │   ├── RuleMysqlRepository.java    
58    │   │       │    │   │   └── TreeMysqlRepository.java
59    │   │       │    │   ├── RuleRepository.java 
60    │   │       │    │   └── TreeRepository.java 
61    │   │       │    └── util
62    │   │       │        └── CacheUtil.java
63    │   │       ├── interfaces
64    │   │       │    ├── dto
65    │   │       │    │   ├── DecisionMatterDTO.java
66    │   │       │    │   └── TreeDTO.java    
67    │   │       │    └── DDDController.java
68    │   │       └── DDDApplication.java
69    │   └── resources    
70    │       ├── mybatis
71    │       └── application.yml
72    └── test
73         └── java
74             └── org.itstack.demo.test
75                 └── ApiTest.java

演示部分重点代码块,完整代码下载关注公众号;bugstack虫洞栈 | 回复DDD落地

application应用层

application/MallRuleService.java | 应用层定义接口服务,也可以适当做简单包装

1/**
 2 * 商超规则过滤服务;提供规则树决策功能
 3 * 微信公众号:bugstack虫洞栈 | 专注原创技术专题案例
 4 * 论坛:http://bugstack.cn
 5 * Create by 付政委 on @2019
 6 */
 7public interface MallRuleService {
 8
 9    /**
10     * 决策服务
11     * @param matter 决策物料
12     * @return       决策结果
13     */
14    EngineResult process(final DecisionMatter matter);
15
16}

domain领域层

domain中有两个领域服务;规则树信息领域、规则执行领域,通过合理的抽象化来实现高内聚、低耦合的模块化服务

domain/service/MallRuleServiceImpl.java | 领域层中的service来实现应用层接口

1/**
 2 * 规则树服务;提供规则规律功能
 3 *
 4 * 1、rule包下只进行规则决策领域的处理
 5 * 2、封装决策行为到领域模型中,外部只需要调用和处理结果即可
 6 * 3、可以扩展不同的决策引擎进行统一管理
 7 *
 8 * 微信公众号:bugstack虫洞栈 | 专注原创技术专题案例
 9 * 论坛:http://bugstack.cn
10 * Create by 付政委 on @2019
11 */
12@Service("mallRuleService")
13public class MallRuleServiceImpl implements MallRuleService {
14
15    private Logger logger = LoggerFactory.getLogger(MallRuleServiceImpl.class);
16
17    @Resource(name = "ruleEngineHandle")
18    private EngineFilter ruleEngineHandle;
19
20    @Override
21    public EngineResult process(DecisionMatter matter) {
22        try {
23            return ruleEngineHandle.process(matter);
24        } catch (Exception e) {
25            logger.error("决策引擎执行失败", e);
26            return new EngineResult(false);
27        }
28    }
29
30}

domain/service/logic/LogicFilter.java | 逻辑决策定义

1/**
 2 * 微信公众号:bugstack虫洞栈 | 专注原创技术专题案例
 3 * 论坛:http://bugstack.cn
 4 * Create by 付政委 on @2019
 5 */
 6public interface LogicFilter {
 7
 8    /**
 9     * 逻辑决策器
10     * @param matterValue          决策值
11     * @param treeNodeLineInfoList 决策节点
12     * @return                     下一个节点Id
13     */
14    Long filter(String matterValue, List<TreeNodeLineInfo> treeNodeLineInfoList);
15
16    /**
17     * 获取决策值
18     *
19     * @param decisionMatter 决策物料
20     * @return               决策值
21     */
22    String matterValue(DecisionMatter decisionMatter);
23
24}

domain/service/engine/EngineFilter.java | 引擎执行定义

1

infrastructure基础层

1、实现领域层仓储定义

2、数据库操作为非业务属性的功能操作

3、在仓储实现层进行组合装配DAO&Redis&Cache等

infrastructure/repository/RuleRepository.java

1/**
 2 * 微信公众号:bugstack虫洞栈 | 专注原创技术专题案例
 3 * 论坛:http://bugstack.cn
 4 * Create by 付政委 on @2019
 5 */
 6public interface EngineFilter {
 7
 8    EngineResult process(final DecisionMatter matter) throws Exception;
 9
10}

interfaces接口层

1、包装应用接口对外提供api

2、外部传输对象采用DTO类,主要为了避免内部类被污染{不断的迭代的需求会在类中增加很多字段}

3、目前依然是提供的Http服务,如果提供的rpc服务,将需要对外提供可引用jar

interfaces/DDDController.java

1**
 2 * 微信公众号:bugstack虫洞栈 | 欢迎关注学习专题案例
 3 * 论坛:http://bugstack.cn
 4 * Create by 付政委 on @2019
 5 */
 6@Controller
 7public class DDDController {
 8
 9    private Logger logger = LoggerFactory.getLogger(DDDController.class);
10
11    @Resource
12    private MallTreeService mallTreeService;
13    @Resource
14    private MallRuleService mallRuleService;
15
16    /**
17     * 测试接口:http://localhost:8080/api/tree/queryTreeSummaryInfo
18     * 请求参数:{"treeId":10001}
19     */
20    @RequestMapping(path = "/api/tree/queryTreeSummaryInfo", method = RequestMethod.POST)
21    @ResponseBody
22    public ResponseEntity queryTreeSummaryInfo(@RequestBody TreeDTO request) {
23        String reqStr = JSON.toJSONString(request);
24        try {
25            logger.info("查询规则树信息{}Begin req:{}", request.getTreeId(), reqStr);
26            TreeCollect treeCollect = mallTreeService.queryTreeSummaryInfo(request.getTreeId());
27            logger.info("查询规则树信息{}End res:{}", request.getTreeId(), JSON.toJSON(treeCollect));
28            return new ResponseEntity<>(treeCollect, HttpStatus.OK);
29        } catch (Exception e) {
30            logger.error("查询规则树信息{}Error req:{}", request.getTreeId(), reqStr, e);
31            return new ResponseEntity<>(e.getMessage(), HttpStatus.OK);
32        }
33    }
34
35    /**
36     * 测试接口:http://localhost:8080/api/tree/decisionRuleTree
37     * 请求参数:{"treeId":10001,"userId":"fuzhengwei","valMap":{"gender":"man","age":"25"}}
38     */
39    @RequestMapping(path = "/api/tree/decisionRuleTree", method = RequestMethod.POST)
40    @ResponseBody
41    public ResponseEntity decisionRuleTree(@RequestBody DecisionMatterDTO request) {
42        String reqStr = JSON.toJSONString(request);
43        try {
44            logger.info("规则树行为信息决策{}Begin req:{}", request.getTreeId(), reqStr);
45            DecisionMatter decisionMatter = new DecisionMatter();
46            decisionMatter.setTreeId(request.getTreeId());
47            decisionMatter.setUserId(request.getUserId());
48            decisionMatter.setValMap(request.getValMap());
49            EngineResult engineResult = mallRuleService.process(decisionMatter);
50            logger.info("规则树行为信息决策{}End res:{}", request.getTreeId(), JSON.toJSON(engineResult));
51            return new ResponseEntity<>(engineResult, HttpStatus.OK);
52        } catch (Exception e) {
53            logger.error("规则树行为信息决策{}Error req:{}", request.getTreeId(), reqStr, e);
54            return new ResponseEntity<>(e.getMessage(), HttpStatus.OK);
55        }
56    }
57
58}

测试验证

规则树结构{数据库转Json} | 规则树结构可按需自行定义

1{
  2    "treeNodeMap": {
  3        "1": {
  4            "nodeType": 1,
  5            "ruleDesc": "用户性别[男/女]",
  6            "ruleKey": "userGender",
  7            "treeId": 10001,
  8            "treeNodeId": 1,
  9            "treeNodeLineInfoList": [
 10                {
 11                    "nodeIdFrom": 1,
 12                    "nodeIdTo": 11,
 13                    "ruleLimitType": 1,
 14                    "ruleLimitValue": "man"
 15                },
 16                {
 17                    "nodeIdFrom": 1,
 18                    "nodeIdTo": 12,
 19                    "ruleLimitType": 1,
 20                    "ruleLimitValue": "woman"
 21                }
 22            ]
 23        },
 24        "11": {
 25            "nodeType": 1,
 26            "ruleDesc": "用户年龄",
 27            "ruleKey": "userAge",
 28            "treeId": 10001,
 29            "treeNodeId": 11,
 30            "treeNodeLineInfoList": [
 31                {
 32                    "nodeIdFrom": 11,
 33                    "nodeIdTo": 111,
 34                    "ruleLimitType": 3,
 35                    "ruleLimitValue": "25"
 36                },
 37                {
 38                    "nodeIdFrom": 11,
 39                    "nodeIdTo": 112,
 40                    "ruleLimitType": 3,
 41                    "ruleLimitValue": "25"
 42                }
 43            ]
 44        },
 45        "12": {
 46            "nodeType": 1,
 47            "ruleDesc": "用户年龄",
 48            "ruleKey": "userAge",
 49            "treeId": 10001,
 50            "treeNodeId": 12,
 51            "treeNodeLineInfoList": [
 52                {
 53                    "nodeIdFrom": 12,
 54                    "nodeIdTo": 121,
 55                    "ruleLimitType": 3,
 56                    "ruleLimitValue": "25"
 57                },
 58                {
 59                    "nodeIdFrom": 12,
 60                    "nodeIdTo": 122,
 61                    "ruleLimitType": 3,
 62                    "ruleLimitValue": "25"
 63                }
 64            ]
 65        },
 66        "111": {
 67            "nodeType": 2,
 68            "nodeValue": "果实A",
 69            "treeId": 10001,
 70            "treeNodeId": 111,
 71            "treeNodeLineInfoList": [ ]
 72        },
 73        "112": {
 74            "nodeType": 2,
 75            "nodeValue": "果实B",
 76            "treeId": 10001,
 77            "treeNodeId": 112,
 78            "treeNodeLineInfoList": [ ]
 79        },
 80        "121": {
 81            "nodeType": 2,
 82            "nodeValue": "果实C",
 83            "treeId": 10001,
 84            "treeNodeId": 121,
 85            "treeNodeLineInfoList": [ ]
 86        },
 87        "122": {
 88            "nodeType": 2,
 89            "nodeValue": "果实D",
 90            "treeId": 10001,
 91            "treeNodeId": 122,
 92            "treeNodeLineInfoList": [ ]
 93        }
 94    },
 95    "treeRoot": {
 96        "treeId": 10001,
 97        "treeName": "购物分类规则树",
 98        "treeRootNodeId": 1
 99    }
100}

通过postman调用 | raw => json

查询规则树信息

测试接口:http://localhost:8080/api/tree/decisionRuleTree

请求参数:{"treeId":10001}

1{
 2    "treeInfo": {
 3        "treeId": 10001,
 4        "treeName": "购物分类规则树",
 5        "treeDesc": "用于分类不同类型用户可购物范围",
 6        "nodeCount": 7,
 7        "lineCount": 6
 8    },
 9    "treeRulePointList": [
10        {
11            "ruleKey": "userGender",
12            "ruleDesc": "用户性别[男/女]"
13        },
14        {
15            "ruleKey": "userAge",
16            "ruleDesc": "用户年龄"
17        }
18    ]
19}

4.jpg

规则树行为信息决策

测试接口:http://localhost:8080/api/tree/decisionRuleTree

请求参数:{"treeId":10001}

1{
2    "userId": "fuzhengwei",
3    "treeId": 10001,
4    "nodeId": 112,
5    "nodeValue": "果实B",
6    "success": true
7}

5.jpg

1  .   ____          _            __ _ _
 2 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
 3( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 4 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
 5  '  |____| .__|_| |_|_| |_\__, | / / / /
 6 =========|_|==============|___/=/_/_/_/
 7 :: Spring Boot ::        (v2.0.5.RELEASE)
 8
 92019-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)
102019-10-19 18:22:05.675  INFO 13820 --- [           main] org.itstack.demo.DDDApplication          : No active profile set, falling back to default profiles: default
112019-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
122019-10-19 18:22:07.756  INFO 13820 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8080 (http)
132019-10-19 18:22:07.870  INFO 13820 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
142019-10-19 18:22:07.870  INFO 13820 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet Engine: Apache Tomcat/8.5.34
152019-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;;.]
162019-10-19 18:22:08.040  INFO 13820 --- [ost-startStop-1] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
172019-10-19 18:22:08.040  INFO 13820 --- [ost-startStop-1] o.s.web.context.ContextLoader            : Root WebApplicationContext: initialization completed in 2088 ms
182019-10-19 18:22:08.102  INFO 13820 --- [ost-startStop-1] o.s.b.w.servlet.ServletRegistrationBean  : Servlet dispatcherServlet mapped to [/]
192019-10-19 18:22:08.126  INFO 13820 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean   : Mapping filter: 'characterEncodingFilter' to: [/*]
202019-10-19 18:22:08.127  INFO 13820 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean   : Mapping filter: 'hiddenHttpMethodFilter' to: [/*]
212019-10-19 18:22:08.127  INFO 13820 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean   : Mapping filter: 'httpPutFormContentFilter' to: [/*]
222019-10-19 18:22:08.127  INFO 13820 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean   : Mapping filter: 'requestContextFilter' to: [/*]
232019-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]
242019-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
252019-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)
262019-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)
272019-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)
282019-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)
292019-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]
302019-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]
312019-10-19 18:22:16.272  INFO 13820 --- [           main] o.s.j.e.a.AnnotationMBeanExporter        : Registering beans for JMX exposure on startup
322019-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
332019-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]
342019-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 ''
352019-10-19 18:22:16.381  INFO 13820 --- [           main] org.itstack.demo.DDDApplication          : Started DDDApplication in 11.458 seconds (JVM running for 20.584)
362019-10-19 18:22:31.336  INFO 13820 --- [nio-8080-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring FrameworkServlet 'dispatcherServlet'
372019-10-19 18:22:31.336  INFO 13820 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : FrameworkServlet 'dispatcherServlet': initialization started
382019-10-19 18:22:31.372  INFO 13820 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : FrameworkServlet 'dispatcherServlet': initialization completed in 36 ms
392019-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"}}
402019-10-19 18:22:32.508  INFO 13820 --- [nio-8080-exec-1] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Starting...
412019-10-19 18:22:32.956  INFO 13820 --- [nio-8080-exec-1] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Start completed.
422019-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
432019-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
442019-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"}
452019-10-19 18:23:36.989  INFO 13820 --- [nio-8080-exec-5] o.itstack.demo.interfaces.DDDController  : 查询规则树信息10001Begin req:{"treeId":10001}
462019-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"}]}
47

综上总结

  • 以上模拟购物场景下的规则处理抽象为树决策引擎,以达到独立领域服务。另外决策服务可以使用Drools,任何抽象并不一定永远使用,不要拘泥于一种形式
  • 一些大型架构设计往往不是换一个设计模型就能彻底提升效率,还是需要人员整体素质,这是一个不断培养的过程
  • 领域驱动设计的思想并不只是教会程序猿写代码,也是非程序员以外的所有互联网人员都适合学习的内容
  • 家里住的舒适不舒适,并不一定取决于三居或者四居,大部分还是依赖于怎么对格局的布置。事必躬亲、亲力亲为的精益求精之路,终究会让你设计出更加合理的代码
  • 通过DDD指导思路与案例代码架构演示来学习领域驱动设计,但全文这些演示代码并不是最重要的,重要的是学会怎么去做一个合理的微服务,哪怕它不是DDD
相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
目录
相关文章
|
设计模式 JSON 架构师
你真的需要防腐层吗?DDD 系统间的7种关系梳理与实践
当提到系统间交互的时候,人们都会想到大名鼎鼎的防腐层,用来防止其他系统的模型变更对本系统造成影响。但是在实践这个模式的过程中,我们常常会遇到问题。此时我们也应该考虑下其他的系统交互方式。
26914 12
你真的需要防腐层吗?DDD 系统间的7种关系梳理与实践
|
缓存 前端开发 中间件
DDD 领域驱动设计落地实践系列:工程结构分层设计
前面几篇文章中,笔者给大家阐述了 DDD 领域驱动设计的三大过程,重点围绕如何通过战略设计与战术设计进行 DDD 落地实践进行了详细的讨论,但是还没有涉及到工程层面的落地。实际上所有的这些架构理论到最后都是为了使得我们代码结构更加清晰,从而开发出 bug 少、扩展性强、逻辑清楚的应用。因此本文就是为了解决 DDD 领域驱动落地实践最后一公里问题,将我们分析出来的领域模型通过与工程结构的映射实现真正的落地。
DDD 领域驱动设计落地实践系列:工程结构分层设计
|
前端开发 测试技术 API
DDD领域驱动设计实战-分层架构及代码目录结构(上)
DDD领域驱动设计实战-分层架构及代码目录结构
1521 0
DDD领域驱动设计实战-分层架构及代码目录结构(上)
|
存储 设计模式 缓存
DDD领域驱动设计实战-分层架构及代码目录结构(下)
DDD领域驱动设计实战-分层架构及代码目录结构
1678 0
DDD领域驱动设计实战-分层架构及代码目录结构(下)
|
4月前
|
缓存 项目管理
项目管理定义问题之DDD架构的分层架构中基础层作用是什么
项目管理定义问题之DDD架构的分层架构中基础层作用是什么
|
消息中间件 架构师 搜索推荐
DDD领域驱动设计的概念解析
DDD领域驱动设计的概念解析
240 1
|
消息中间件 前端开发 小程序
DDD实战之五:战略设计之上下文映射和系统分层架构(下)
DDD实战之五:战略设计之上下文映射和系统分层架构(下)
DDD实战之五:战略设计之上下文映射和系统分层架构(下)
|
JSON 缓存 监控
代码分层设计
在搭建一个项目之前,除了要进行架构和业务方面的设计和分析,往往还需要对代码的结构进行规范化设计。而分层思想,是应用系统最常见的一种架构模式。
605 0
|
前端开发 小程序 机器人
DDD实战之五:战略设计之上下文映射和系统分层架构(上)
DDD实战之五:战略设计之上下文映射和系统分层架构(上)
DDD实战之五:战略设计之上下文映射和系统分层架构(上)
|
前端开发 Oracle 关系型数据库
【代码分层结构设计】思想学习
本文主要摘录个人写代码的心得体会。
160 0