16 | 视图:如何实现服务和数据在微服务各层的协作?
16 | 视图:如何实现服务和数据在微服务各层的协作?
讲述:欧创新
时长17:24大小11.93M
服务的协作
1. 服务的类型
2. 服务的调用
3. 服务的封装与组合
4. 两种分层架构的服务依赖关系
数据对象视图
总结
思考题
赞 15
提建议
精选留言(90)
- 瓜瓜2019-11-20各种数据对象的转换放在那一层,很重要,比如vo与dto的转换放在前端应用,dto与do的转换放在用户接口层或者是应用层(根据用户接口层与应用层发生调用,还是微服务之间应用层发生调用而定),领域层只有DO,DO与PO的转换放在仓储实现里面,基础层只操作PO,至于仓储层的实现是放在领域层还是基础层,可以根据具体情况而定,放在基础层则为严格分层,放在领域层,则方便微服务的拆分和组合。望老师指正
作者回复: 理解的非常好。
共 2 条评论20 - zj2019-11-29应用层其实我觉得入参数是DTO比较好,因为应用层是要暴漏给其他微服务调用的。然后在应用层将DTO转为DO来调用领域服务。如果调用其他微服务,则构造对方服务需要的DTO来调用。
作者回复: 如果是你说的这种情况,也是可以的。
共 8 条评论14 - 。2019-11-20欧老师你好 用户接口层:入参是DTO,内部将DTO转化为DO后调用应用层,将应用层的结果转化为VO后返回给前台 应用层:入参是DO,返回值是DO 领域层:入参是DO,返回值是DO 基础层:入参是DO,内部将DO转化成PO进行数据库的增删改查,执行结果用PO去映射,再转化为DO作为基础层的返回值 问题1:时间范围查询时,会有辅助字段,如:beginTime和endTime,PO这怎么处理?我们的处理方式是增删改用PO,查询时候用QueryPO,QueryPO继承了PO并额外增加用于查询的辅助字段(比如时间、集合、模糊查询等),这样可以么? 问题2:有的查询功能,比如按照名称查询,查询条件就是name,DTO、DO和PO是一样的,也需要在每一层都去转化一下么?我们把查询时的对象命名为QueryPO,从用户接口层到基础层的入参都是这一个,这样可以么?展开
作者回复: 1、可以的。 2、是否要做数据转换?主要是考虑解耦,这样各层不必受其它层的数据限制,它类似齿轮,通过数据转换来做适配。如果从前端到后端数据对象都是一样的,用一个对象其实也是可以的。要结合自己的场景来分析。
共 9 条评论14 - 胖大蟲2019-12-25用户接口层会完成 DO 和 DTO 的互转,那不就等于将DO暴露给用户接口层了?按我的理解,DO从领域服务出来的时候就应该转换为DTO给应用层,从应用层开始往上(包括应用层)都不知道DO的存在;DO和DTO的互转,由领域服务负责,接收上层(应用层)传递的DTO,转换为DO,进而调用DO的方法完成业务逻辑,再将需要返回的数据转换为DTO返回给上层
作者回复: DTO放应用层或者用户接口层都是可以的,保证外层依赖内层的原则就可以了,这些对象都在同一个微服务内。当应用服务调用其它微服务的应用服务的时候,DO和DTO的转换就在应用层。
共 5 条评论7 - 张迪2019-11-20传入数据的格式校验放在哪层做?例如手机号格式校验、姓名长度校验等
作者回复: 前端可以做简单格式校验,稍微复杂数据的校验在应用层。业务规则和逻辑校验在领域层。 你说的这两个前端就可以校验。
共 7 条评论7 - Keith2021-04-17DO-PO转换为什么要放在基础层? 这样的话基础层岂不是要理解领域层的逻辑, 进而对上层形成了依赖?共 1 条评论6
- 阿信2020-04-20关于数据对象视图定义,这块的想法和老师稍微有点区别 我的想法: https://www.processon.com/view/link/5e85bc85e4b0412013f87eb6 应用层对外是DTO,DO层不暴露到Facade层。
作者回复: DTO在应用层和用户接口层都是可以的,因为在应用服务调用其他微服务的应用服务的时候,就需要完成在应用层完成DO到DTO的转换。至于DO与DTO的转换在应用层还是接口层,个人感觉两层都可以,根据具体场景选择。
5 - lamthun2020-05-26老师, 你好, 按照上图中的服务调用与数据组合的思路. 看图示领域层只关心repository, 不关心缓存, 缓存还是由业务层进行封装, 是这样吗? 如果是这样子的话, 在大部分应用系统中, 领域层会不会又变成薄薄的增删查改这样的一层.
作者回复: 一般来说查询类型的操作不具有太多的领域逻辑,所以这类服务一般不放在领域层,领域层主要是基于聚合根ID的简单查询和聚合数据的变更操作。所以在DDD里面出现了读写分离模式,将复杂查询操作单独拿出来。或者你也可以直接在应用服务中实现复杂查询。
共 2 条评论4 - okjesse2019-12-19请问应用层需要访问repository层返回一些查询数据时,repository是只能返回DO,还是说也可以返回为DTO呢,谢谢。
作者回复: repository返回的一般都是DO。 复杂查询可以采用CQRS读写分离的模式。
共 3 条评论4 - xxx2021-07-25说说实战下来的感受:严格按照DTO -> DO -> PO,服务再分成 controller -> application -> domain -> repository 确实是职责划分的很清晰,是很好的设计。规定上层只能调用下层,不能跨层调用,确实是很严的规则,也确实能够实现上层只是基于下层在做编排,写起来代码很短很优雅。 另外,各层的数据对象往往是通过注解来完成其职责的。比如DTO中要加上验证的注解、Json的注解,PO中要加上JPA或者MyBatis的注解。把这些注解都堆到一个对象里,确实是职责不清,逻辑很混乱;并且这些对象或多或少在字段上有些差异(更不要说DO还有业务方法了),放到一个对象中要考虑各种trick才能解决,所以分开是势在必行的。但是对象转来转去也确实是心累…… 不过这么分层,实际是对原来的 controller -> service -> dao 加了一个 domain层,所谓贫血模型到充血模型,本质上就是把service里面的逻辑移到领域对象中。层层这么调下去的话,就要再加一层的模板代码,写起来好累啊…… 因此在实践中,如果当前的逻辑就是CRUD,我会简化一点:应用层就直接调用repository,DTO和PO直接互转,不需要创建DO。当需要DO来处理业务逻辑时,再正经地创建DO。背后的思考如下:在简单CRUD场景中,CRUD它就是业务逻辑(查询往往会有一些花样)!所以在应用层编写一些JPA查询逻辑,也不是特别不能接受。。。 这么来看,原来的DDD的分层调用图中,可以跨层调用,也是有合理性的。展开共 2 条评论4
- 珅珅君2020-06-04如果需要依赖第三方的接口,应该放在哪,领域服务还是应用服务
作者回复: 微服务之间的服务调用一般放在应用层通过应用服务来进行服务组合和编排。服务调用的关系图里面有微服务调用微服务外的应用服务的路线的。
共 2 条评论3 - 墨名次2019-11-20在数据试图这里,如果有用户User,那么在后端代码中是不是会有: com.xxx.xxx.po.User com.xxx.xxx.do.User com.xxx.xxx.dto.User 或者为了方便区分则可以: com.xxx.UserPO com.xxx.User com.xxx.UserDTO ?展开
作者回复: 是的,但是有的时候不一定是一对一的关系。
3 - iMARS2020-10-10看完这一节有一个感觉,如果系统的业务不复杂,或者属于从0到1发展阶段的,DDD的设计方式会拖慢开发的速度,增加系统的复杂度,不适合用DDD的方式。仅仅是VO-DTO-DO-PO之间的转换就存在效能的损耗,并增加了开发工作量。而对于业务复杂,又需要规模化弹性扩展的,需要引入DDD的方式对已有系统采用自下而上的方式进行重构,以便做到业务敏捷。
作者回复: 没错。这些操作主要是为了实现各层的解耦,保证领域模型的稳定,能够快速适配前端应用和后端资源逻辑的变化。所以你需要根据具体的业务场景以及性能等要求来平衡是否要做这些对象的转换,其实某些业务需求和逻辑不容易变化的业务场景是可以不做转换的。
2 - 小孩2020-08-13没太看懂这里vo跟dto区别,我的用法一般是前段传vo过来转换成do处理然后po持久化,如果中间需要模型转换一个中间过程会创建dto
作者回复: VO主要用于前端展示。如果有多个前端应用的话,我们可以通过DTO去进行前端应用的接口和数据适配。用户接口层的Facade接口和DTO的作用主要如下: 在微服务面向不同前端应用时,同样的一段业务逻辑,可能由于渠道不同,而在前端展示的页面要素不同,因此要求后端微服务返回的数据结果会不同。比如在面向内部员工的PC端应用时,可能要求返回某些对象的全部属性的数据。而面向外部客户的移动端应用,可能只需要返回几个关键属性的数据就可以了。 为了避免暴露微服务的核心业务逻辑,防止数据外泄,你不能将后端对象的所有属性数据,不加区分的暴露给所有前端应用。你更不能仅仅因为前端应用不同的数据展示需求,而在后端定制开发出多个不同的应用服务或领域服务,面向前端应用提供不同的服务适配。这样容易产生大量重复代码,也容易导致业务逻辑混乱,代码可维护性变差。 这时用户接口层的Facade服务和数据组装器Assembler就可以发挥作用了。Facade服务可以封装应用服务,适配不同前端应用的集成技术体系,提供不同类型的服务接口适配,而数据组装器Assembler可以根据不同前端应用的数据需求,完成前端DTO和后端DO对象的组装和转换等操作,按需提供数据适配。我们基本不需要调整领域模型的核心领域逻辑,就可以面向不同前端应用提供灵活的接口定制和数据适配。
共 3 条评论2 - 发飙的蜗牛2020-02-29老师,仓储服务返回值应该是DO还是PO呢?如果是PO,那么实体方法去调用就要自己去将PO转为DO,如果是DO的话就需要在仓储实现里面转,但是像spring data JPA这些框架,实现我们是不用去管的,我们只需要泛型化将PO传进去就可以了,但是只能返回PO 另一个问题想问下,对于DDD封层架构,事务控制应该放到那一层去做呢?如果不是放到一层,应该怎么去设计事务控制?
作者回复: 仓储服务应该返回的是PO,然后通过工厂将PO转成DO。跨聚合的事务控制放在应用层。理论上领域层不会有事务,如果避免不了,也可以在领域层增加事务,比如在进行业务数据和事件数据同时写入的时候。
2 - 梦终结2020-01-10老师你好,我想问下: 1、DO里面是充血模型是么? 2、如果要是充血模型,那对DO的最基础的增删改查都写在DO里面是么?
作者回复: 除了增删改查,还有实体的业务行为,具体表现为实体的方法。
共 2 条评论2 - 胡杨2019-12-13领域实体是entity,领域对象是do,那Do就是entity么? 是的话,那po到do的转换一般是EF等框架自己做的了吧?
作者回复: 是的,实体就是DO。 PO与DO的互转,Java大部分都需要自己写代码转换。 .net倒是有一些框架可以自动去做映射,比如你说的EF。
共 2 条评论2 - 沐风2019-12-08各层之间不断转换object 对象,从实现上看还是显得繁琐,欧老师,有简化的实现或实践么?
作者回复: 现在看主要的重复性工作是在对象映射关系的建立。目前似乎还没有好的工具来辅助。
共 3 条评论2 - xj_zh2019-11-21欧老师,时间允许,麻烦讲一下充血模型。
作者回复: 先说一下贫血模型吧,贫血模型是指使用的领域对象中只有自己的属性以及setter和getter方法,业务逻辑都不包含在领域对象中而是放在业务逻辑层。 而充血模型则是将实体相关的业务逻辑都放在实体的方法中实现,实体包含了自身的属性和业务行为,它在领域模型中就是一个具有业务行为和逻辑的实体。充血模型的实体更能体现领域模型的对象行为。
共 2 条评论2 - 。2019-11-20欧老师,微服务之后,前后端分离。前端和后端的登陆认证用什么来做呢?基于token的方式,“退出登录”是假的退出吧?是不是只在前端应用删除了保存的key,对于后端应用,这个key还是生效的
作者回复: 我们前端用JWT实现单点登录。后端微服务的服务调用通过API鉴权来控制。
共 3 条评论2