1、前言
上一篇咱们讲了DDD的核心概念(附上连接),而且设计了咱们的上下文映射图,那么接下来就准备开始立项了,本篇文章的部分知识点可能对一部分人来讲比较基础,能够选择性的阅读。
在这以前咱们日常用的最多的应该就是3层架构了,这里也不展开描述了,你们都是在3层的陪伴下一路走来的~
DDD所使用的传统分层架构是松散分层,也就是上层能够访问任意层级的下层,而不是仅限于当前层的下一层,这是有别于3层架构的。以下面2张图的区别图:
【图1】
【图2】
Application:这层的职责是对接收到的数据作一些非业务性验证,事务的控制,最重要的是协调多个聚合之间的操做。这里应该能够清晰的表达出整个操做所作的事情,而且与通用语言是一致的。
Domain:这一层是DDD设计的核心,这里不但须要精确合理的表达出通用语言的每个细节,另外如何把对象合理的定义为聚合、实体、值对象也是重中之重。这里不但关系着整个项目的复杂度,也是战术建模的体现,任何的一行代码都是对业务的准肯定义,应该是恰到好处。一个清晰简洁的战术建模才能够应对后续的快速变化。
Infrastructure:这里是辅助性的一层,也是整个项目的基础。比如这里存放着一砖一瓦,最终建造什么模样的高楼在于用它的地方。主要包括,仓储的实现(咱们存放数据的地方)、一些通用的支撑性类库。
2、六边形架构
在[Vaughn Vernon]《实现领域驱动设计》一书中屡次提到对DDD主张六边形架构的概念,六边形架构对于保证限界上下文内的领域概念的清晰性有着重要的做用,那么什么是六边形架构,以下图3(摘自[Vaughn Vernon]《实现领域驱动设计》一书)。
【图3】
在当今愈来愈提倡开放合做的大环境下,引用的多样化的Service,和在自身系统达到必定规模以后的分布式治理,愈来愈须要经过协做进行工做,那么如何提高协做的效率变得愈来愈重要。提升各个应用程序的自治性,是一种有效提高协做能力的手段。从上图中看出为了保证领域模型所在的应用程序的干净简洁和自治性,各类适配器做为"防腐层(在上篇中有提到)"在整个程序的最外层保护着当前的“界限上下文(在上篇中有提到)”不受外部入侵。
因此在咱们的整个设计中须要注意对涉及到外部系统交互的地方的抽象,经过面向接口、依赖注入等方式作到外部的变化对自身系统的影响最小化。
3、终于开始建项目了
按照以前的这些描述,咱们终于初步创建了咱们的解决方案。以下图4:
【图4】
这里把每一个项目的职责大体说一下。
Mall:负责咱们的电商网站的界面处理和用户的数据录入
Mall.Application:按模块分别定义不一样的ApplicationService来说述每个操做下的“故事”。
Mall.Application.DomainEventSubscribers:全部的领域事件订阅者。
Mall.Domain:这里存放着战术建模的结晶,Entity、Aggregate、ValueObject。(下面会具体讲述下)
Mall.Domain.Events:全部的领域事件,这层也能够合并到Domain中,给它新建一个文件夹。
Mall.Domain.IRepositories:全部的仓储(资源库)接口。相似于三层中的IDAL。
Mall.DomainService:领域服务,存放着那些不适合放在聚合/值对象上的无状态的操做方法,用于实现特定某个领域的任务。
Mall.Infrastructure:存放着一些通用类库
Mall.Infrastructure.Repositories:全部仓储(资源库)的实现。
Mall.Infrastructure.Translators:翻译层,也就是与外部系统沟通的桥梁,主要的职责就是作好“反腐层”的重任。
4、DDD中的3个臭皮匠
这里的3个臭皮匠其实就是:Entity、ValueObject、Aggregate。咱们要提炼出业务中的精华,合理的抽象为这3个概念,而且这种抽象是需随着领域里的概念变化而变化的。这3者的结合运用会让咱们的项目活起来,这是DDD的核心。这里再把这3个概念从新梳理一下。
Entity(实体): 每一个实体是惟一的,而且能够至关长的一段时间内持续地变化。咱们能够对实体作屡次修改,故一个实体对象可能和它先前的状态大不相同。可是,因为它们拥有相同的身份标识,他们依然是同一个实体。
ValueObject(值对象):值对象用于度量和描述事物,当你只关心某个对象的属性时,该对象即可做为一个值对象。实体与值对象的区别在于惟一的身份标识和可变性。
Aggregate(聚合):聚合类是实体的升级,是由一组与生俱来就密切相关实体和值对象组合而成的,这整个组合的最上层实体就是聚合。
5、CQRS(Command Query Responsibility Segregation)
说到DDD必然要提一下CQRS,我认为CQRS和DDD的关系就像咖啡和牛奶,给大型系统的构建提供了一剂良药,它生于读写分离,具备高吞吐量、高伸缩性等特色,值得咱们为之付出一些代价。可是CQRS的使用会使整个数据持久化和查询的链路拉长,而且工做量也会比简单的读写一体化大的多,因此须要对项目作出合理的考量来决定是否使用。
当咱们须要把某个复杂的聚合修改以后写入到数据库的时候,要保证N张表的数据被同时修改为功,整个事务的周期必然会加长。并且当咱们须要显示来自不一样聚合类型与实例的数据时,咱们的SQL必然包含N多的join。领域越复杂这种状况愈加常见。
CQRS须要和事件源结合使用,对数据的修改操做只是往事件源里增长一条修改后的结果记录(相似于咱们的源码控制软件的log),并不会直接把修改后的对象持久化到数据库。这样可以大大提升数据修改的速度,而且对于查询操做的实现方式就比较多样化了。大体列举了如下4种方式:
1.仍是使用单个数据库,每次领域对象的获取都须要根据事件源中的事件集合作重建,获得当前的最新的数据返回。这只是编码设计上的读写分离
2.拆分为读库和写库,实现方式同1
3.拆分为读库和写库,而且针对读库作专门的查询数据冗余,异步的经过事件源来修改查询数据,能够结合merge commit。
4.在3的基础上作读库的负载均衡
这4种方式复杂度各不相同,能够结合实际项目的复杂度择优选择,其中最关键的一条即是是否存在大量的列表类数据展现,若是是那么1和2便就不适合了。在以上的方式以外能够结合其余的数据存储一块儿使用,如缓存,NoSql,然而这只须要订阅全部的命令事件便可实现。
6、结语
本篇主要介绍了项目的分层架构、每层的职责和里面存放什么样子的类。限于非咱们这个系列的核心主题,因此都没有发散出去作更加具体形象的描述,但愿你们能够边结合[Vaughn Vernon]《实现领域驱动设计》一书的阅读跟着我作实际的编码来加深对DDD的理解。跳出根深蒂固的三层思想是痛苦的,可是我认为只要坚持下去,DDD会让你看见一片世外桃源,到那时会以为咱们的付出都是值得的。而且DDD思想的运用可大可小,小到类的设计,大到复杂项目之间的构架,都会给你提供帮助。
做者:Zachary
出处:https://zacharyfan.com/archives/114.html
▶关于做者:张帆(Zachary,我的微信号:Zachary-ZF)。坚持用心打磨每一篇高质量原创。欢迎扫描右侧的二维码~。
按期发表原创内容:架构设计丨分布式系统丨产品丨运营丨一些思考。
若是你是初级程序员,想提高但不知道如何下手。又或者作程序员多年,陷入了一些瓶颈想拓宽一下视野。欢迎关注个人公众号「跨界架构师」,回复「技术」,送你一份我长期收集和整理的思惟导图。
若是你是运营,面对不断变化的市场一筹莫展。又或者想了解主流的运营策略,以丰富本身的“仓库”。欢迎关注个人公众号「跨界架构师」,回复「运营」,送你一份我长期收集和整理的思惟导图。