1. DDD的架构
1. 用户接口层(Controller层)
用户接口层负责向用户显示信息和解释用户指令。
2. 应用层(Service层)
应用层是很薄的一层,理论上不应该由业务规则或逻辑,主要面向用例和流程相关的操作。也可以完成
- 编排多个聚合服务和领域对象完成业务操作;
- 调用其他微服务的应用服务,完成微服务之间服务的组合和编排;
应用服务是在应用层的,它负责服务的组合、编排和转发,负责处理业务用例的执行顺序以及结果的拼装,以粗粒度的服务通过 API 网关向前端发布。还有,应用服务还可以进行安全认证、权限校验、事务控制、发送或订阅领域事件等。
3. 领域层(domain层)
领域层的作用是实现企业核心业务逻辑,通过各种校验手段保证业务的正确性。领域层主要体现领域模型的业务能力,它用来表达业务概念,业务状态和业务规则;
领域层包含聚合根,实体,值对象。领域服务等领域模型中的领域对象;
领域模型的业务逻辑主要是由实体和领域服务来实现的:
- 实体会采用充血模型来实现所有与之相关的业务功能。
- 实体和领域对象在实现业务逻辑上是同级的,当领域中的某些功能,单一实体(或者值对象)不能实现时,领域服务就会出马他可以组合聚合内的多个实体(或者值对象),实现复杂的业务逻辑。
4. 基础层(Repository)
基础层是贯穿所有层的,它的作用就是为其它各层提供通用的技术和基础服务,包括第三方工具、驱动、消息中间件、网关、文件、缓存以及数据库等。比较常见的功能还是提供数据库持久化。
基础层包含基础服务,它采用依赖倒置设计,封装基础资源服务,实现应用层、领域层与基础层的解耦,降低外部资源变化对应用的影响。
- 比如说,在传统架构设计中,由于上层应用对数据库的强耦合,很多公司在架构演进中最担忧的可能就是换数据库了,因为一旦更换数据库,就可能需要重写大部分的代码,这对应用来说是致命的。那采用依赖倒置的设计以后,应用层就可以通过解耦来保持独立的核心业务逻辑。当数据库变更时,我们只需要更换数据库基础服务就可以了,这样就将资源变更对应用的影响降到了最低。
2. 传统架构迁移到DDD架构
传统企业应用大多是单体架构,而单体架构大多是三层架构。。三层架构解决了程序内代码间调用复杂、代码职责不清的问题,但这种分层是逻辑概念,在物理上它是中心化的集中式架构,并不适合分布式微服务架构。
DDD 分层架构中的要素其实和三层架构类似,只是在 DDD 分层架构中,这些要素被重新归类,重新划分了层,确定了层与层之间的交互规则和职责边界。
DDD 分层架构在用户接口层引入了 DTO,给前端提供了更多的可使用数据和更高的展示灵活性。
DDD 分层架构将业务逻辑层的服务拆分到了应用层和领域层。应用层快速响应前端的变化,领域层实现领域模型的能力。
另外一个重要的变化发生在数据访问层和基础层之间。三层架构数据访问采用 DAO 方式;DDD 分层架构的数据库等基础资源访问,采用了仓储(Repository)设计模式,通过依赖倒置实现各层对基础资源的解耦。
仓储又分为两部分:仓储接口和仓储实现。仓储接口放在领域层中,仓储实现放在基础层。原来三层架构通用的第三方工具包、驱动、Common、Utility、Config 等通用的公共的资源类统一放到了基础层。
项目级微服务的内部遵循分层架构模型就可以了。领域模型的核心逻辑在领域层实现,服务的组合和编排在应用层实现,通过 API 网关为前台应用提供服务,实现前后端分离。但项目级的微服务可能会调用其它微服务,你看在下面这张图中,比如某个项目级微服务 B 调用认证微服务 A,完成登录和权限认证。
通常项目级微服务之间的集成,发生在微服务的应用层,由应用服务调用其它微服务发布在 API 网关上的应用服务。你看下图中微服务 B 中红色框内的应用服务 B,它除了可以组合和编排自己的领域服务外,还可以组合和编排外部微服务的应用服务。它只要将编排后的服务发布到 API 网关供前端调用,这样前端就可以直接访问自己的微服务了。
3. 杂谈
ddd领域驱动设计用人话怎么讲。
别犟,无论是传统mvc的先设计表结构还是ddd的先设计领域层在设计表结构。归根到底最终还是要设计表结构的。只不过ddd一直给人一种感觉设计领域对象图更厉害的样子——“你看我可是画了领域图,可不是er图哟”
那么透过现象看本质,所有业务目的不就是为了curd吗?一般来说数据库会有一张基表,n个辅表。甚至在数据中台,只有一张打平表。那么什么样的数据要抽取成为辅表。什么样的数据配置是表属性?是开发自己定还是根据领域划分定的区别。
一般来说,有些数据会高并发写操作,我们新开一个辅表,有些数据不会变更。我们可能会违反数据库范式将其作为冗余属性配置到表记录中。
那么ddd中领域层就很好解释通,我们将聚合根作为基表,将其他实体看作辅表,将值对象看着表记录的属性。其实就是ddd中实体的变与值对象的不变。分析领域行为的目的其实就是有依据的建立表…不像mvc上来开发根据产品需求自己去建表。
说下聚合这回事。就是整体与部分的关系。订单明细不能独立于订单,它们就是聚合关系。用户可以独立于订单 ,它们就不是聚合关系。
说下值对象和实体,按理说没必要纠结。重点考虑目前这个字段是否经常变化。实体也不一定会有自己单独的表,(挂在表上其实本质就和值对象一样)只是概念区分,没必要太较真。
说下聚合根和实体,本质都是实体,只不过聚合根是基表,实体是辅表。比如我创建订单一定会创建订单明细,我删除订单一会会删除订单明细。且对领域的增删改一般是通过聚合根实现的。不能直接去操作实体。
说下充血对象的事情 ,其实我一直以来有疑问。不过大致现在想明白些,校验分为三种:参数格式校验交给充血对象、借助其他领域服务校验交由应用层、领域内其他实体的校验交由领域服务。
说下充血对象和贫血对象,贫血对象简单粗暴,充血对象昂贵而优雅。一般来说领域模型中出现类似继承、多态的情况,则应该继承与多态的部分以充血对象的形式进行实现。
说下ddd工厂,首先不是设计模式的工厂模式,你可以理解就是创建一个领域对象的方法集合(spring bean)一般由应用层调用将dto转化成do 、基础设施层调用将多个po填充为do。
说下ddd对齐语言的事情。业务不会关心你的几表和n个辅表。他们就知道订单对象里面有一个订单明细的集合。而我们的domain object或者领域代码就是这样表现的。其订单表和订单明细的详细操作逻辑封装在仓库(基础设施层)
再说下ddd解决的是增删改的问题。查询操作直接走基层设施层封装的方法即可。
最后说下软件就是校验+入库。ddd说入库操作应该解耦,于是交给了仓库。还剩下校验,说应该充实对象。不要做贫血对象。于是一部分交给了do、又说编排工作应该交给应用层,于是跨领域服务的校验交给了应用层 (但是应用层说:我是你x吗?)于是交给了工厂,应用层不需要关注领域对象创建的细节、那么领域层也不能啥也不做,于是领域层完成的是聚合实体间的校验。
最后说下:ddd不会落地的人讲概念。会落地的人还是讲概念。这就是生活吧。