作者:小傅哥

博客:https://bugstack.cn

沉淀、分享、成长,让自己和他人都能有所收获!

一、前言

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

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

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

二、案例目标

本案例通过一个商品下单规则的场景来进行演示DDD;

  1. 假设产品需求业务运行人员可以对不同的商品配置一些规则,这些规则可以满足不同用户类型可以下单不同商品。
  2. 另外一些行为规则是会随着业务发展而增加或者变动的,所以不能写死{写死太吓人了}。
  3. 数据库的PO类不应该被外部服务调用,这也是必须的。如果你开发过很多系统,那么可能已经吃过亏并意识到这个问题。
  4. 按照DDD思想我们尝试需要设计一个规则引擎的服务,通过给外部提供非常简单的接口(application)来获取最终结果。
  5. 通过这样的案例可以很容易的感受到目前的四层架构确实在实现DDD思想上有很多的帮助。

如图;DDD分层结构 | 指导设计架构

三、DDD思想 · 开发设计

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

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

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

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

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

如图;领域开发设计服务

四、工程模型

  1. itstack-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. */
  7. public interface MallRuleService {
  8. /**
  9. * 决策服务
  10. * @param matter 决策物料
  11. * @return 决策结果
  12. */
  13. EngineResult process(final DecisionMatter matter);
  14. }

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")
  13. public class MallRuleServiceImpl implements MallRuleService {
  14. private Logger logger = LoggerFactory.getLogger(MallRuleServiceImpl.class);
  15. @Resource(name = "ruleEngineHandle")
  16. private EngineFilter ruleEngineHandle;
  17. @Override
  18. public EngineResult process(DecisionMatter matter) {
  19. try {
  20. return ruleEngineHandle.process(matter);
  21. } catch (Exception e) {
  22. logger.error("决策引擎执行失败", e);
  23. return new EngineResult(false);
  24. }
  25. }
  26. }

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

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

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

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

infrastructure基础层

  1. 实现领域层仓储定义
  2. 数据库操作为非业务属性的功能操作
  3. 在仓储实现层进行组合装配DAO&Redis&Cache等

infrastructure/repository/RuleRepository.java

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

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
  7. public class DDDController {
  8. private Logger logger = LoggerFactory.getLogger(DDDController.class);
  9. @Resource
  10. private MallTreeService mallTreeService;
  11. @Resource
  12. private MallRuleService mallRuleService;
  13. /**
  14. * 测试接口:http://localhost:8080/api/tree/queryTreeSummaryInfo
  15. * 请求参数:{"treeId":10001}
  16. */
  17. @RequestMapping(path = "/api/tree/queryTreeSummaryInfo", method = RequestMethod.POST)
  18. @ResponseBody
  19. public ResponseEntity queryTreeSummaryInfo(@RequestBody TreeDTO request) {
  20. String reqStr = JSON.toJSONString(request);
  21. try {
  22. logger.info("查询规则树信息{}Begin req:{}", request.getTreeId(), reqStr);
  23. TreeCollect treeCollect = mallTreeService.queryTreeSummaryInfo(request.getTreeId());
  24. logger.info("查询规则树信息{}End res:{}", request.getTreeId(), JSON.toJSON(treeCollect));
  25. return new ResponseEntity<>(treeCollect, HttpStatus.OK);
  26. } catch (Exception e) {
  27. logger.error("查询规则树信息{}Error req:{}", request.getTreeId(), reqStr, e);
  28. return new ResponseEntity<>(e.getMessage(), HttpStatus.OK);
  29. }
  30. }
  31. /**
  32. * 测试接口:http://localhost:8080/api/tree/decisionRuleTree
  33. * 请求参数:{"treeId":10001,"userId":"fuzhengwei","valMap":{"gender":"man","age":"25"}}
  34. */
  35. @RequestMapping(path = "/api/tree/decisionRuleTree", method = RequestMethod.POST)
  36. @ResponseBody
  37. public ResponseEntity decisionRuleTree(@RequestBody DecisionMatterDTO request) {
  38. String reqStr = JSON.toJSONString(request);
  39. try {
  40. logger.info("规则树行为信息决策{}Begin req:{}", request.getTreeId(), reqStr);
  41. DecisionMatter decisionMatter = new DecisionMatter();
  42. decisionMatter.setTreeId(request.getTreeId());
  43. decisionMatter.setUserId(request.getUserId());
  44. decisionMatter.setValMap(request.getValMap());
  45. EngineResult engineResult = mallRuleService.process(decisionMatter);
  46. logger.info("规则树行为信息决策{}End res:{}", request.getTreeId(), JSON.toJSON(engineResult));
  47. return new ResponseEntity<>(engineResult, HttpStatus.OK);
  48. } catch (Exception e) {
  49. logger.error("规则树行为信息决策{}Error req:{}", request.getTreeId(), reqStr, e);
  50. return new ResponseEntity<>(e.getMessage(), HttpStatus.OK);
  51. }
  52. }
  53. }

五、测试验证

规则树结构{数据库转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. }

规则树行为信息决策

测试接口: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. }

服务端

  1. . ____ _ __ _ _
  2. /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
  3. ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
  4. \\/ ___)| |_)| | | | | || (_| | ) ) ) )
  5. ' |____| .__|_| |_|_| |_\__, | / / / /
  6. =========|_|==============|___/=/_/_/_/
  7. :: Spring Boot :: (v2.0.5.RELEASE)
  8. 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)
  9. 2019-10-19 18:22:05.675 INFO 13820 --- [ main] org.itstack.demo.DDDApplication : No active profile set, falling back to default profiles: default
  10. 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
  11. 2019-10-19 18:22:07.756 INFO 13820 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http)
  12. 2019-10-19 18:22:07.870 INFO 13820 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat]
  13. 2019-10-19 18:22:07.870 INFO 13820 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet Engine: Apache Tomcat/8.5.34
  14. 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;;.]
  15. 2019-10-19 18:22:08.040 INFO 13820 --- [ost-startStop-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
  16. 2019-10-19 18:22:08.040 INFO 13820 --- [ost-startStop-1] o.s.web.context.ContextLoader : Root WebApplicationContext: initialization completed in 2088 ms
  17. 2019-10-19 18:22:08.102 INFO 13820 --- [ost-startStop-1] o.s.b.w.servlet.ServletRegistrationBean : Servlet dispatcherServlet mapped to [/]
  18. 2019-10-19 18:22:08.126 INFO 13820 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: 'characterEncodingFilter' to: [/*]
  19. 2019-10-19 18:22:08.127 INFO 13820 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: 'hiddenHttpMethodFilter' to: [/*]
  20. 2019-10-19 18:22:08.127 INFO 13820 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: 'httpPutFormContentFilter' to: [/*]
  21. 2019-10-19 18:22:08.127 INFO 13820 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: 'requestContextFilter' to: [/*]
  22. 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]
  23. 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
  24. 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)
  25. 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)
  26. 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)
  27. 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)
  28. 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]
  29. 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]
  30. 2019-10-19 18:22:16.272 INFO 13820 --- [ main] o.s.j.e.a.AnnotationMBeanExporter : Registering beans for JMX exposure on startup
  31. 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
  32. 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]
  33. 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 ''
  34. 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)
  35. 2019-10-19 18:22:31.336 INFO 13820 --- [nio-8080-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring FrameworkServlet 'dispatcherServlet'
  36. 2019-10-19 18:22:31.336 INFO 13820 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : FrameworkServlet 'dispatcherServlet': initialization started
  37. 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
  38. 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"}}
  39. 2019-10-19 18:22:32.508 INFO 13820 --- [nio-8080-exec-1] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Starting...
  40. 2019-10-19 18:22:32.956 INFO 13820 --- [nio-8080-exec-1] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Start completed.
  41. 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
  42. 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
  43. 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"}
  44. 2019-10-19 18:23:36.989 INFO 13820 --- [nio-8080-exec-5] o.itstack.demo.interfaces.DDDController : 查询规则树信息10001Begin req:{"treeId":10001}
  45. 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,任何抽象并不一定永远使用,不要拘泥于一种形式
  • 一些大型架构设计往往不是换一个设计模型就能彻底提升效率,还是需要人员整体素质,这是一个不断培养的过程
  • 领域驱动设计的思想并不只是教会程序猿写代码,也是非程序员以外的所有互联网人员都适合学习的内容
  • 家里住的舒适不舒适,并不一定取决于三居或者四居,大部分还是依赖于怎么对格局的布置。事必躬亲、亲力亲为的精益求精之路,终究会让你设计出更加合理的代码

七、推荐阅读

Java开发架构篇:DDD模型领域层决策规则树服务设计的更多相关文章

  1. Java开发架构篇《初识领域驱动设计DDD落地》

    作者:小傅哥 博客:https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获! 一.前言 DDD(Domain-Driven Design 领域驱动设计)是由Eric Eva ...

  2. Java开发架构篇:领域驱动设计架构基于SpringCloud搭建微服务

    作者:小傅哥 博客:https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获! 一.前言介绍 微服务不是泥球小单体,而是具备更加清晰职责边界的完整一体的业务功能服务.领域驱动 ...

  3. [Abp vNext 源码分析] - 5. DDD 的领域层支持(仓储、实体、值对象)

    一.简要介绍 ABP vNext 框架本身就是围绕着 DDD 理念进行设计的,所以在 DDD 里面我们能够见到的实体.仓储.值对象.领域服务,ABP vNext 框架都为我们进行了实现,这些基础设施都 ...

  4. DDD模型领域WF到领域层(十五)

    实现超市的结算系统: 计算相应的优惠方式的接口 public interface ICompute { double GetResultTotalMoney(double TotalMoney); } ...

  5. java 开发基础篇1环境安装--eclipse安装教程

    如何安装java环境 http://jingyan.baidu.com/article/a24b33cd59b58e19fe002bb9.html JDK download http://www.or ...

  6. Unity3D 游戏开发架构篇 ——性格一流的设计和持久性

    在游戏开发.游戏人物占了非常大的空间.所有内容都是由主角可以说游戏驱动. 下面来介绍一下一流的设计和持久性的作用. 一.应用场景 游戏中的角色类型不一而足,有不同的技能.有不同的属性等等.有些一个玩家 ...

  7. java 面试架构篇

    1.非功能需求会考虑哪些? 可用性.扩展性.性能: 2.有没有遇到过建了索引反而变慢的情况? 3.从哪些角度去设计系统? 4.代码中使用过的设计模式?

  8. World Wind Java开发之十三——加载Geoserver发布的WMS服务(转)

    这篇是转载的平常心博客,原地址见:http://www.v5cn.cn/?p=171 1.WMSTiledImageLayer类说明 一个WMSTiledImageLayer类对象只能对应一个WMS发 ...

  9. 清晰架构(Clean Architecture)的Go微服务: 设计原则

    我最近写了一个Go微服务应用程序,这个程序的设计来自三个灵感: 清晰架构"Clean Architecture"¹ and SOLID (面向对象设计)² 设计 原则³ Sprin ...

随机推荐

  1. MySQL中出现Unknow column 'xx' in field list的解决办法

    首先创建一个表,然后插入数据发现出错误 经过多次尝试发现title前面多了一个空格 我们把空格去掉,然后在插入数据,发现数据创建成功

  2. async,await与task.wait()或task.Result的区别

    你是否曾经与我一样不理解async,await与task.wait()或者task.Result的区别? 接下来,一个Demo让你看出他们之间的区别. static void Main(string[ ...

  3. Scrapy中的crawlspider

    crawlspider 能自动的获取url并提交请求 命令:scrapy genspider -t crawl spidername 'example.cn' 所导入的模块 # -*- coding: ...

  4. 【转帖】Python 重复造轮子/造轮子找模子,你都应该熟读该文

    Chardet,字符编码探测器,可以自动检测文本.网页.xml的编码. colorama,主要用来给文本添加各种颜色,并且非常简单易用. Prettytable,主要用于在终端或浏览器端构建格式化的输 ...

  5. nginx与keepalived实现高可用+Apache实现负载均衡

    nginx与keepalived实现高可用 本实验使用了四台虚拟机 两台需要安装nginx及keepalived 两台安装Apache nginx可以源码安装也可以用yum安装nginx yum安装n ...

  6. MATLAB学习1 之画图函数

    ezplot适用条件 "ezplot"命令可以用于显函数.隐函数和参数方程作图. 不同函数的使用格式 显函数y=f(x),ezplot函数的调用格式为ezplot(f, [xmin ...

  7. linux和windows互传文件、用户配置文件和密码配置文件、用户组管理、用户管理...

    linux和windows互传文件 第一种:在linux主机上下载lrzsz软件包 1.yum install lrzsz 2.通过rz命令上传window的文件到linux主机上 用过sz 文件名下 ...

  8. C++入门编程题目 NO.1

    题目:有1.2.3.4个数字,能组成多少个互不相同且无重复数字的三位数?都是多少? 1.程序分析:可填在百位.十位.个位的数字都是1.2.3.4.组成所有的排列后再去 掉不满足条件的排列. #incl ...

  9. Python安装第三方模块出错 No module named setuptools

    在安装 zabbix-alerta 第三方模块时候报错 python setup.py install 此时需要安装 setuptools 模块, 这里用自动化脚本安装 wget https://bo ...

  10. zabbix分布式安装全过程

    项目规划 软件 版本 IP zabbix-server 3.4.15 10.1.10.128 zabbix-proxy 3.4.15 10.1.10.129 zabbix-agent 3.4.15 1 ...