【DDD】领域驱动设计实践 —— 框架实现
本文主要了在社区服务系统(ECO)中基于SpringMVC+mybatis框架对DDD的落地实现。本文为系列文章中的其中一篇,其他内容可参考:通过业务系统的重构实践DDD。
框架实现图

该框架实现基本和DDD的指导思想契合,主要分为四层,且将关注点放在了domain层。下面将逐层介绍各个组件的职责。
框架详述
User Interface层
门面层,对外以各种协议提供服务,该层需要明确定义支持的服务协议、契约等。包含:
dto
包括request和response两部分,通过它定义入参和出参的契约,在dto层可以使用基础设施曾的validation组件完成入参格式校验;
controller
支持不同访问协议的控制器实现,比如:http/restful风格、tcp/二进制流协议、mq消息/json对象等等。
controller使用基础设施层公共组件完成许多通用的工作:
- 调用RequestMapping(SpringMVC公共组件)完成servlet路由;
- 调用checklogin完成登录态/权限校验;
- 调用logging组件完成日志记录;
- 调用message-resource组件完成错误信息转义,支持I18N;
application层
service
应用服务层,组合domain层的领域对象和基础设施层的公共组件,根据业务需要包装出多变的服务,以适应多变的业务服务需求。
应用服务层主要访问domain领域对象,完成服务逻辑的包装。
应用服务层也会访问基础设施层的公共组件,如rabbitmq,完成领域消息的生产等。
assembler
组装器,负责将多个domain领域对象组装为需要的dto对象,比如查询帖子列表,需要从Post(帖子)领域对象中获取帖子的详情,还需要从User(用户)领域对象中获取用户的基本信息。
组装器中不应当有业务逻辑在里面,主要负责格式转换、字段映射等职责。
domain层
业务领域层,是我们最应当关心的一层,也是最多变的一层,需要保证这一层是高内聚的。确保所有的业务逻辑都留在这一层,而不会遗漏到其他层。按照ddd(domain driven design)理论,主要有如下概念构成:
domain entity
领域实体。有唯一标识,可变的业务实体对象,它有着自己的生命周期。比如社区这一业务领域中,‘帖子’就是一个业务实体,它需要有一个唯一性业务标识表征,同时他的状态和内容可以不断发生变化。
domain value object
领域值对象。可以没有唯一性业务标识,且一旦定义,他是不可变的,它通常是短暂的。这和java中的值对象(基本类型和String类型)类似。比如社区业务领域中,‘帖子的置顶信息’可以理解为是一个值对象,不需要为这一值对象定义独立的业务唯一性标识,直接使用‘帖子id‘便可表征,同时,它只有’置顶状态‘和’置顶位置‘,一旦其中一个属性需要发生变化,则重建值对象并赋值给’帖子‘实体的引用,不会对领域带来任何负面影响。
domain factory
领域对象工厂。用于复杂领域对象的创建/重建。重建是指通过respostory加载持久化对象后,重建领域对象。
domain service
领域服务。区别于应用服务,他属于业务领域层。
可以认为,如果某种行为无法归类给任何实体/值对象,则就为这些行为建立相应的领域服务即可。比如:转账服务(transferService),需要操作借方/贷方两个账户实体。
传统意义上的util static方法中,涉及到业务逻辑的部分,都可以考虑归入domain service。
domain event
领域事件。领域中产生的一些消息事件,通过事件通知/订阅的方式,可以在性能和解耦层面得到好处。
repository
仓库。我们将仓库的接口定义归类在domain层,因为他和domain entity联系紧密。仓库用户和基础实施的持久化层交互,完成领域对应的增删改查操作。
仓库的实际实现根据不同的存储介质而不同,可以是redis、oracle、mongodb等。
鉴于现在社区服务的存储介质有三套:oracle、redis、mongodb,且各个存储介质的字段属性名不一致,因此需要使用translator来做翻译,将持久化层的对象翻译为统一的领域对象。
translator
翻译器。将持久化层的对象翻译为统一的领域对象。
翻译器中不应当有业务逻辑在里面,主要负责格式转换、字段映射等职责。
infrastructure层
基础设施层提供公共功能组件,供controller、service、domain层调用。
repository impl
对domain层repository接口的实现,对应每种存储介质有其特定实现,如oracle的mapper,mongodb的dao等等。repository impl会调用mybatis、mongo client、redis client完成实际的存储层操作。
checkLogin
权限校验器,判定客户端是否有访问该资源的权限。提供给User Interface层的Controller调用。
exception
异常分类及定义,同时提供公共的异常处理逻辑,具体由ExceptionHandler实现。
transport
transport完成和第三方服务的交互,可以有多种协议形式的实现,如http+json、tcp+自定义协议等,配套使用的还有Resolver解析器,用于对第三方服务的请求和响应进行适配,提供一个防腐层(AnticorruptionLayer,DDD原书P255)的作用。
transcation
提供事务管理,交给Spring管理。
logging
日志模块,记录trace日志,使用log4j完成。
message resource
消息资源管理,交给Spring统一管理。
模块结构
本节介绍ECO系统的模块结构,由于是使用java语言实现,也就是java工程中的包结构,可以直观地看出框架的落地实现效果。

各个package的详细解释参考上节框架详述,着重注意如下几点:
- factory是专职为model服务的,因此放入对应的entity的modle package中;
- domain.repository包里面只有仓库的接口定义,实际的实现交给了infrastrcture中的repository module,该做法被称作"依赖倒置",好处在于确保domoain层语义完整,同时对确保业务领域的一致性也有帮助,再者可以在domain实现内存形式的repository哑实现,从而让domain可以真正脱离掉infrastructure;
- translator module正常情况下不需要,但在重构老系统时,往往需要隔离掉存储模型对业务模型的影响,使用translator来讲存储模型翻译为业务模型;
- infrastructure.repository作为仓库层,会将实体的增删改查操作委托给具体的存储层服务,如oracle对应的mapper实现,mongodb对应dao实现,还有redis的实现;
- ECO中domain event的生产者和消费者实现均交给infrastructure.rabbitmq package实现。
【DDD】领域驱动设计实践 —— 框架实现的更多相关文章
- DDD领域驱动设计和实践(转载)
-->目录导航 一. DDD领域驱动设计介绍 1. 什么是领域驱动设计(DDD) 2. 领域驱动设计的特点 3. 如果不使用DDD? 4. 领域驱动设计的分层架构和构成要素 5. 事务脚本和领域 ...
- 【DDD】领域驱动设计实践 —— UI层实现
前面几篇blog主要介绍了DDD落地架构及业务建模战术,后续几篇blog会在此基础上,讲解具体的架构实现,通过完整代码demo的形式,更好地将DDD的落地方案呈现出来.本文是架构实现讲解的第一篇,主要 ...
- DDD 领域驱动设计-谈谈 Repository、IUnitOfWork 和 IDbContext 的实践(3)
上一篇:<DDD 领域驱动设计-谈谈 Repository.IUnitOfWork 和 IDbContext 的实践(2)> 这篇文章主要是对 DDD.Sample 框架增加 Transa ...
- DDD 领域驱动设计-谈谈 Repository、IUnitOfWork 和 IDbContext 的实践(1)
好久没写 DDD 领域驱动设计相关的文章了,嘎嘎!!! 这几天在开发一个新的项目,虽然不是基于领域驱动设计的,但我想把 DDD 架构设计的一些东西运用在上面,但发现了很多问题,这些在之前的短消息项目中 ...
- DDD 领域驱动设计-谈谈 Repository、IUnitOfWork 和 IDbContext 的实践(转)
http://www.cnblogs.com/xishuai/p/ddd-repository-iunitofwork-and-idbcontext.html 好久没写 DDD 领域驱动设计相关的文章 ...
- 基于事件驱动的DDD领域驱动设计框架分享(附源代码)
原文:基于事件驱动的DDD领域驱动设计框架分享(附源代码) 补充:现在再回过头来看这篇文章,感觉当初自己偏激了,呵呵.不过没有以前的我,怎么会有现在的我和现在的enode框架呢?发现自己进步了真好! ...
- DDD 领域驱动设计-谈谈 Repository、IUnitOfWork 和 IDbContext 的实践(2)
上一篇:<DDD 领域驱动设计-谈谈 Repository.IUnitOfWork 和 IDbContext 的实践(1)> 阅读目录: 抽离 IRepository 并改造 Reposi ...
- DDD领域驱动设计落地实践(十分钟看完,半小时落地)
一.引子 不知今年吹了什么风,忽然DDD领域驱动设计进入大家视野.该思想源于2003年 Eric Evans编写的"Domain-Driven Design领域驱动设计"简称DDD ...
- 浅谈我对DDD领域驱动设计的理解
从遇到问题开始 当人们要做一个软件系统时,一般总是因为遇到了什么问题,然后希望通过一个软件系统来解决. 比如,我是一家企业,然后我觉得我现在线下销售自己的产品还不够,我希望能够在线上也能销售自己的产品 ...
随机推荐
- 基于NFS实现WordPress
实验内容: (1)主机IP nfs server IP :192.168.29.120 nfs server IP: 192.168.29.110 (2)要求 nfs server共享/data/we ...
- 一小时学会ECMAScript6新特性
ECMAScript 简介 简称es,是一套标准,javascript就是使用这套标准的语言.主流的浏览器使用的是ECAMScript5,ECAMScript6(ECAMScript2015)是一涛新 ...
- 24. leetcode 409. Longest Palindrome
409. Longest Palindrome Given a string which consists of lowercase or uppercase letters, find the le ...
- CSS3伪类实现动画旋转效果
一个简单的动画效果demo,keyframes为关键帧,图片贴在代码下方.利用了伪类实现css3动画效果,初学者可以看一下,恩.<!doctype html> <html lang= ...
- VS2010 常见错误类型汇总
开发路漫漫,尤其对于刚从事开发不久的新手来说,常常遇到一些稀奇古怪的错误,很是头疼,鉴于自己在开发过程中常遇到的几个错误做个分享,希望对大家有所帮助: 错误1: 在创建完win32的DLL后,编译时出 ...
- Mapreduce——视频播放数据分类统计
很多视频网站都有电视剧热度排名,一般是依据用户在自己站的行为数据所体现出的受欢迎程度来排名.这里有一份来自优酷.爱奇艺.搜索视频等五大视频网站的一份视频播放数据,我们利用这份数据做些有意义的事情. 金 ...
- jvm参数解析(含调优过程)
前阵 对底层账单系统进行了压测调优,调优的最后一步--jvm启动参数中,减小了线程的堆栈空间:-XX:ThreadStackSize=256K,缩减至原来的四分之一,效果明显,不过并没有调 ...
- SpringBoot构建RESTful service完成Get和Post
一个基本的RESTfule service最进场向外提供的请求Method就是Get和Post. 在Get中,常用的都会在请求上带上参数,或者是路径参数.响应Json. 在Post中,常用的会提交fo ...
- 利用webpack构建vue项目
快速搭建vue项目 一,确认自己有无搭建好node以及npm环境,这些是前提,具体安装方法可参考https://nodejs.org/en/. 二,开始构建项目. 第1步:新建一个文件夹,随意命名. ...
- SpringMVC中文件的上传(上传到服务器)和下载问题(二)--------下载
一.建立一个简单的jsp页面. 我们在建好的jsp的页面中加入一个超链接:<a href="${pageContext.request.contextPath}/download&qu ...