DDD 这几年越来越火,资料也很多,大部分的资料都偏向于理论介绍,有给出的代码与传统 MVC 的三层架构差异较大,再加上大量的新概念很容易让初学者望而却步。本文从 MVC 架构角度来讲解如何演进到 DDD 架构。
在网站制作、成都做网站过程中,需要针对客户的行业特点、产品特性、目标受众和市场情况进行定位分析,以确定网站的风格、色彩、版式、交互等方面的设计方向。成都创新互联公司还需要根据客户的需求进行功能模块的开发和设计,包括内容管理、前台展示、用户权限管理、数据统计和安全保护等功能。
代码角度:
项目管理角度:
以上的问题越来越严重,很多人开始把眼光转向 DDD,于是埋头啃了几本大部头的书,对以下概念有了基本的了解:
于是把 MVC 架构进行了改造,演进成 DDD 的分层架构。
DDD 分层架构:
MVC 架构到 DDD 分层架构的映射:
至此,算了基本入门了 DDD 架构,扩展性也得到了一定的提升。不过随着业务的发展,不断冒出新的问题:
带着问题不断学习他人经验,并不断的尝试,逐渐 get 到以下技能:
领域(domain)是个模块,包含以下组成部分,传统的 service 按功能可能拆分到任何一个地方,各司其职。
(1) 聚合根(AggregateRoot)
聚合本身也是一个实体,聚合可以包含其他实体,其他实体不能脱离聚合而单独提供服务,比如一篇文章下的评论,评论必须从属与文章,没有文章也就没有评论。仓库层(repository)也必须是以聚合为核心提供服务的;
(2) 仓库服务(repository)
资源库是聚合的仓储机制,外部世界通过资源库,而且只能通过资源库来完成对聚合的访问。资源库以聚合的整体管理对象。因此,一个聚合只能有一个资源库对象,那就是以聚合根命名的资源库。除此之外的其他对象,都不应该提供资源库对象。仓储服务的实现一般有 Spring Data JPA、Mybatis 两种方式。
如果是用 Spring Data JPA 实现,直接使用 JPA 注解 @OneToOne、@OneToMany,配合 fetch 配置,即可一个方法查询出所有的关联实体。
如果是用 Mybatis 实现,那么 repository 需要加入多个 mapper 的引用,再手动做拼装。
这里有一个经典的 Hibernate 笛卡尔积问题,答案是在聚合根中,一般不会加在大量的关联实体对象。如果确实需要查询关联对象而关联对象又比较多怎么办呢?在 DDD 中有一个 CQRS (Command-Query Responsibility Segregation) 模式,是一种读写分离模式,在此场景中需要将查询操作放到查询命令中分页查询。
当然 CQRS 也是一个很复杂模式,不应照搬他人方案,而是根据自己的业务场景选择适合自己的方案,以下列举了 CQRS 的几种应用模式:
(3) 工厂服务(factory)
作用是创建聚合,只传入必要的参数,工厂服务内部隐藏复杂的创建逻辑。简单的聚合可以直接通过 new、静态方法等创建,不是必须由 factory 创建。
(4) 领域服务
单个实体对象能处理的逻辑放到实体里,多个实体或有交互的场景放到领域服务里。
领域服务可不可以调用仓储层或外部接口? 可以,但不能直接和领域服务代码放一起,领域服务模块存放 API,实现放基础层(infrastructure)。
领域服务对象不建议直接以聚合名 + DomainService 命名,而要以操作命令关联,比如用户保存服务命名为:UserSaveService, 审核服务:UserAuditSerivce。
应用层通过应用服务接口来暴露系统的全部功能。在应用服务的实现中,它负责编排和转发,它将要实现的功能委托给一个或多个领域对象来实现,它本身只负责处理业务用例的执行顺序以及结果的拼装。通过这样一种方式,它隐藏了领域层的复杂性及其内部实现机制。
比如下订单服务的方法:
public void submitOrder(Long orderId) {
Order order = OrderFetchService.fetchById(orderId); //获取订单对象
OrderCheckSerivce.check(order); //验证订单是否有效
OrderSubmitSerivce.submit(order); //提交订单
ShoppingCartClearService.clear(order); //移除购物车中已购商品
NotifySerivce.emailNotify(order.getUser()); //发送邮件通知买家
}
对于复杂的业务来说,应用层也有几种模式:
基础层是比较简单一层,不过这里还有个比较疑惑的问题:按照 DDD 的四层架构图去划分 Maven 模块,基础层是最上的一层,但是基础层也要包含基础组件供其他层使用,这时基础层应该是放到最下层,直接按照这样构建 Maven 模块会造成循环依赖。
相比来说,另一个架构图更准确一些,不过依然没有直观体现 Maven 模块如何划分。
我的最佳实践是将基础层拆分两部分,一部分是基础的组件 + 仓储 API,一部分是实现,maven 模块划分图如下所示:
经过以上的两层的磨炼,恭喜你把 DDD 战术都学习完了,应付日常的代码开发也够了,不过作为架构师来说,探索的道路还不能止步于此,接下来会 DDD 战略部分。战略部分关注点有 3 个:
统一语言的重要性可以根据 Jeff Patton 在《用户故事地图》中给出的一副漫画来直观的描述:
统一语言是提炼领域知识的输出结果,也是进行后续需求迭代及重构的基础,统一语言的建立有以下几个要点:
(1) 统一语言必须以文档的形式提供出来,并且在整个项目组的各团队达成共识;
(2) 统一语言必须每个中文名有对应的英文名,并且在整个技术栈保持一致;
(3) 统一语言必须是完整的,包含以下要素:
以事件风暴的形式(Event Storming),列出所有的用户故事(Use Story),用户故事可通过 6W 模型来构建,即描写场景的 Who、What、Why、Where、When 与 hoW 六个要素。然后圈选功能相近的部分,就形成了领域,领域又根据职能不同划分为:核心域、支撑域、通用域,
具体的过程有很多参考资料,这里不在细讲,最终的输出是领域划分图,以下是一个保险业务示例:
限界上下文包含两部分:上下文(Context)是业务目标,限界(Bounded)则是保护和隔离上下文的边界。
比如上图中的实现部分即是限界上下文的边界,虚线部分代表了领域的边界。限界上下文没有统一的划分标准,需要的读者根据自己的业务场景来甄别如何划分。
一个上下文中包含了相同的领域知识,角色在上下文中完成动作目标;
边界体现在以下几方面:
DDD 架构作为一套先进的方法论,在很多场景能发挥很大价值,但是 DDD 也不是银弹。高级的架构师把 DDD 架构当成一种工具,结合其他架构经验一起为业务服务。
DDD 的不足有几个方面:
本文从 MVC 架构开始讲述了如何从演进到 DDD 架构,限于篇幅很多 DDD 的知识点没有讲到,希望大家在实践过程中能灵活运用,尽享 DDD 给业务带来的价值。
分享文章:从MVC到DDD的架构演进
转载注明:http://www.csdahua.cn/qtweb/news34/526934.html
网站建设、网络推广公司-快上网,是专注品牌与效果的网站制作,网络营销seo公司;服务项目有等
声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 快上网