网站采集到wordpress,免费发布广告信息的网站,网页升级紧急通知页面,淄博建设企业网站领域驱动设计落地 写模型代码结构interfaces 用户接口层facade 接口assembler 组装器 application 应用层service 应用层服务 domain 领域层Xxx 聚合entity 领域对象aggregate 聚合根entity 实体valobj 值对象 repository 仓储po 持久化对象mapperservicefacade 仓储接口persis… 领域驱动设计落地 写模型代码结构interfaces 用户接口层facade 接口assembler 组装器 application 应用层service 应用层服务 domain 领域层Xxx 聚合entity 领域对象aggregate 聚合根entity 实体valobj 值对象 repository 仓储po 持久化对象mapperservicefacade 仓储接口persistence 仓储实现 infrastructure 基础层util 工具constant 常量 读模型代码结构读写模型主要差异 问题汇总 写模型代码结构
com.myj.drug.xxx
├─Application.java
├─interfaces
| ├─facade
| | └XxxController.java
| ├─assembler
| | └PersonAssembler.java
├─infrastructure
| ├─util
| ├─constant
├─domain
| ├─Xxx
| | ├─service
| | | ├─XxxDomainService.java
| | | └XxxFactory.java
| | ├─repository
| | | ├─po
| | | | └XxxPO.java
| | | ├─persistence
| | | | └XxxRepositoryImpl.java
| | | ├─mapper
| | | | └XxxMapper.java
| | | ├─service
| | | | └XxxServiceImpl.java
| | | ├─facade
| | | | └XxxRepositoryInterface.java
| | ├─event
| | | ├─XxxEvent.java
| | | └XxxEventType.java
| | ├─entity
| | | ├─aggregate
| | | ├─entity
| | | ├─valobj
├─application
| ├─service
| | └XxxApplicationService.javainterfaces 用户接口层
用户接口层是前端应用与微服务应用层的桥梁通过 Facade 接口封装应用服务适配前端并提供灵活的服务完成 DO 和 DTO(Query/Command) 相互转换
当应用服务接收到前端请求数据时组装器会将 DTO(Query/Command) 转换为 DO。当应用服务向前端返回数据时组装器会将 DO 转换为 DTO。
facade 接口
实际上对应的就是三层架构的Controller层
assembler 组装器
将 DTO(Query/Command) 转换为 DO 或者 DO 转换为 DTO(Query/Command) 的工具类
application 应用层
应用层位于领域层之上主要负责组合和编排领域层服务
应用层也是微服务之间交互的通道它可以调用其它微服务的应用服务完成微服务之间的服务组合和编排
service 应用层服务
调用领域层服务或者调用其他微服务的服务
domain 领域层
领域层的作用是实现企业核心业务逻辑通过各种校验手段保证业务的正确性。领域层主要体现领域模型的业务能力它用来表达业务概念、业务状态和业务规则。
领域层包含聚合根、实体、值对象、领域服务等领域模型中的领域对象。
领域模型的业务逻辑主要是由实体和领域服务来实现的其中实体会采用充血模型来实现所有与之相关的业务功能。
实体主要实现单一业务逻辑
领域服务主要实现多实体之间的复杂业务逻辑
Xxx 聚合
某个高度业务内聚性的聚合里面包含聚合根、实体、值对象、领域服务等领域模型中的领域对象
entity 领域对象
主要包含聚合根、实体、值对象
aggregate 聚合根
主要包含聚合根实体负责管理聚合内所有实体的生命周期例如初始化持久化等等
entity 实体
主要包含实体依附于聚合根实体的生命周期但是有唯一标识一般需要落库
valobj 值对象
主要包含值对象没有唯一标识一般跟着聚合根落库是聚合根对应表的部分字段没有实际的对应表结构
repository 仓储
一个聚合一个仓储实现聚合数据的持久化。领域服务通过仓储接口来访问基础资源由仓储实现完成数据持久化和初始化。仓储一般包含仓储接口和仓储实现
po 持久化对象
与表一一对应的java对象
mapper
使用mybatis-plus框架,每个PO对象都用对应的Mapper对象
service
使用mybatis-plus框架,每个PO对象都用对应的Service对象其实单用mapper也可以但是生成这个对象可以使用很多已经实现的数据持久化功能可以提高开发效率
facade 仓储接口
为了解耦业务逻辑和基础资源在基础层和领域层之间增加一层仓储服务实现依赖倒置。通过这一层可以实现业务逻辑和基础层资源的依赖分离。在变更基础层数据库的时候只要替换仓储实现就可以了上层核心业务逻辑不会受基础资源变更的影响从而实现依赖倒置。
但是用于为了方便领域内的不同聚合之前拆分把仓储实现也放到了领域层里面
主要是实现数据持久化的统一接口一般只包含savefindByIdremove 3个方法对应的是聚合根的持久化从内存恢复聚合根或者删除等等
由于业务常常包含更新操作如果每次都通过save进行更新操作会产生不必要的字段更新或者导致数据不一致所以特地再加上update方法用于聚合根的更新持久化
除了上述的4个方法可能还会有其他的查询方法而这个查询方法不是面向接口调用而是其他领域服务需要用的查询方法
persistence 仓储实现
对仓储接口的实现主要基于上面所说的 mybatis-plus 的 mapperservice 去实现
infrastructure 基础层
基础层是贯穿所有层的它的作用就是为其它各层提供通用的技术和基础服务包括第三方工具、驱动、消息中间件、网关、文件、缓存以及数据库等。比较常见的功能还是提供数据库持久化。但是由于上面说的为了方便领域模型的聚合拆分也可以把仓储实现放到领域层
基础层包含基础服务它采用依赖倒置设计封装基础资源服务实现应用层、领域层与基础层的解耦降低外部资源变化对应用的影响。
util 工具
代码中需要用到的工具类
constant 常量
代码中需要用到的常量
读模型代码结构
├─DrugSystemQueryApplication.java
├─interfaces
| ├─facade
| | └UserController.java
├─infrastructure
| ├─utils
| | └DefaultUtil.java
| ├─enums
| | └Default.java
├─application
| ├─service
| | └UserQueryApplicationServiceImpl.java
| ├─repository
| | ├─service
| | | └UserServiceImpl.java
| | ├─po
| | | └UserPO.java
| | ├─persistence
| | | └UserRepositoryImpl.java
| | ├─mapper
| | | └UserMapper.java
| | ├─facade
| | | └UserRepository.java读写模型主要差异
读模型代码结构删掉了领域层仓储统一放到应用层其余于写模型基本一直
问题汇总 在DDD的原则里repository操作的都是聚合根repository的作用就是把内存中的聚合根持久化或者把持久化的数据还原为内存中的聚合根。repository中一般也只有getByIdsave,remove几个方法。例如取消订单的场景我其实只需要更新order的状态等少数几个字段但是如果调用repository的save方法就会把订单其他字段以及订单明细数据都更新一次这样就会造成性能影响以及数据冲突的问题。 在repository增加只更新部分字段的方法就是上文我们说的update方法虽然这样会对repository有一定的污染但是实现比较简单 消费mq的逻辑应该属于那一层 消息订阅方一般在应用层监听和接受事件数据 订单父单和子单设计成一个聚合好还是2个聚合好 一般来说订单和订单明细是在一个聚合里面的 聚合根与领域服务在职责上有些重叠了在实现的时候如何选择 理论上聚合根方法和领域服务都可以组合多个实体对象完成复杂的领域逻辑。但为了避免聚合根的业务逻辑过于复杂避免聚合根类代码量过于庞大建议聚合根除了承担它的聚合管理职能外只作为实体实现与聚合根自身行为相关的业务逻辑。而将跨多个实体的复杂领域逻辑放在领域服务中实现。简单聚合的跨实体领域逻辑可以考虑在聚合根方法中实现 仓储为什么不是放在基础层 理论上仓储都应该放到基础层的。将它们放到领域层的聚合目录下主要是基于以下考虑一个聚合会有一个仓储以后微服务的演进基本上会以聚合为单位进行重组或者拆分所以在代码拆分的时候以聚合目录进行整体迁移就可以了这样在代码上不会花太多的时间 复杂的查询怎么办 复杂的查询不建议放在领域层去解决。可以采用CQRS也就是常说的读写分离的模式或者直接在应用层完成复杂查询。本次我们实现的就是读写分离模式。 领域事件怎么实现 同一个项目的微服务内不同聚合之间采用事件总线方式(SpringEvent)不同项目的微服务之间采用消息队列模式 事务控制放在哪里合适 聚合内可以在领域服务采用事务机制聚合之间或微服务之间可以在应用服务采用事务机制 是不是每个微服务都要有自己单独的数据库 没有特殊考虑的话建议一个微服务一个数据库但是由于目前我们不动数据库所以还是共用一个数据库但是代码层面要把他当成不同数据库来对待不同微服务的数据访问一定要通过不同微服务所暴露的服务来获取不可以直接操作其他微服务的数据表 使用Dubbo应该将哪一层的服务进行封装api 个人认为应该将应用层服务进行api封装不同微服务需要互相访问的时候导入相应的api包即可里面包含了req和resp等入参和出参的对象 想问下在工厂里可以使用MapStruct来简化PO和Entity的数据初始化和持久化的转换吗 这个工具应该可以用来做DO和PO的转换不过本次我们没用使用 复杂查询放在哪里 由于查询一般没有太多的业务逻辑和规则控制所以一般复杂的查询都会放在应用层 层间调用关系 依赖原则是外层依赖内层。例如不能从领域层去调应用层的服务