在当前的开发者社区,广泛流行一种被Martin Fowler称为贫血领域模型的构架模式。该模式由于大师的批判而饱受指责。这个模式有个致命的缺陷:在处理复杂领域时常常表现不佳。很多迹象表明,当我们面对复杂应用时,最好还是转向一个基于丰富领域模型的构架。
尽管丰富领域模型有着显而易见的好处,但也给实践带来了挑战,这既有构建技术上的原因,也有设计方法上的原因。对于构建技术,如Annotation、Aspect和DI等复杂技术的使用,最终能够清晰的被掌握,但在设计方法上,往往由于实践的不同而难以取得共识。
本文的目的仅仅是在技术上给出一种由贫血领域模型向丰富领域模型转换的方案,供有相同需要的同行参考。
1.前提和限制<o:p></o:p>
无论使用哪种领域模型,通常都需要借助一些工具的支持,这些工具包括Ioc容器和O/R映射工具。因作者经验所限,当提及这些工具时,只意味着Java世界里的Spring和Hibernate。
有关贫血领域模型的论述请参考:
http://www.martinfowler.com/bliki/AnemicDomainModel.html
贫血领域模型的最佳实践请参考:
https://appfuse.dev.java.net
丰富领域模型的最权威指南请访问:
http://www.domaindrivendesign.org
由于语境的不同,不同的分层模式的术语具有不同的含义,但它们所要实现的任务,是可以分离出来的,而这些任务,目前还没有造成广泛的语义混乱。为了更好的比较本文中提及的两种构架模式,将它们所要完成的任务定义如下:
任务
|
描述
|
表现逻辑
|
接收来自系统外部的请求,将请求代理到其他模块,并将返回的处理结果,通过某种方式呈现给请求者。
|
应用逻辑
|
是用例的外观的实现,协调用例的真正实现者完成一次应用程序相关的功能。
|
领域逻辑
|
对问题领域最本质内容进行建模,实现体现用户核心价值的功能。
|
持久化逻辑
|
与数据的外部存储交互。
|
基础服务
|
更加的以技术为中心,为软件系统的各模块提供基础支持。
|
2.贫血领域模型构架<o:p></o:p>
2.1.分层模式<o:p></o:p>
即使同样打着贫血模型的标签,它们也会有不同的风格。下面是比较典型的一种:
层
|
任务
|
对象
|
描述
|
表现层
|
表现逻辑
|
模型对象
|
Model。领域层中的实体/值对象,也可以用独立的对象。
|
视图
|
View
|
控制器
|
Controller。通过某种机制而获得对用户请求的响应。
|
领域层
|
在领域逻辑中混合了应用逻辑
|
服务
|
Service。同时处理领域逻辑和应用逻辑
|
实体
|
Entity。领域模型的静态视图
|
数据源层
|
持久化逻辑
|
DAO对象
|
有很多实现方式,如JDBC、Hibernate、iBATIS、JPA、EJB CMP
|
基础设施层
|
基础服务
|
<o:p> </o:p>
|
<o:p> </o:p>
|
可以参考下面的示意图:[见附件1]
2.2.示例代码<o:p></o:p>
现在假设有一个在线购物网站,我们要浏览产品列表,然后选中了一个感兴趣的产品,此时我们需要查看该产品的详细信息。
1)页面发出请求
下面是一个可能的JSF代码片断:
<h:commandlink value="#{product.name}" action="#{product.edit}"></h:commandlink>
<f:param value="#{product.id}" name="productid"></f:param>
|
2)分派到控制器
假设请求被调度到控制器yourpackage.action.ProductAction:
package yourpackage.action;<o:p></o:p>
......<o:p></o:p>
public class ProductAction extends BaseAction {<o:p></o:p>
//通过依赖注入的服务<o:p></o:p>
private ProductService productService;<o:p></o:p>
//作为模型对象的领域对象<o:p></o:p>
private Product product;<o:p></o:p>
private Integer id;<o:p></o:p>
//响应查看产品信息的事件<o:p></o:p>
public String edit() {<o:p></o:p>
product = productService.getProduct(id);<o:p></o:p>
return “product”; //JSF将其解析为product.xhtml视图<o:p></o:p>
}<o:p></o:p>
}
|
3)领域层的服务
一般会有一个服务接口ProductService,然后是该接口的实现ProductServiceImpl:
package yourpackage.service;
......
public class ProductServiceImpl extends BaseService implements ProductService {
//通过依赖注入的DAO对象
private ProductDao productDao;
//获取产品信息的领域逻辑方法
public Product getProduct(Integer id) {
return productDao.getProduct(id);
}
}
|
4)领域层的实体
下面是JPA风格的实体,该实体仅用于承载数据,而没有领域行为(贫血之说由此而来)。
package yourpackage.model;
......
@Entity
public class Product {
@Id @GeneratedValue(strategy=GenerationType.AUTO)
@Column(name="Id")
public Long getId() {
return id;
}
<o:p> </o:p>
@Column(name="Name", length=30, nullable=false)
public String getName() {
return name;
}
}
|
5)DAO对象
这里同样需要一个接口,其实现类如果用JPA的话:
package yourpackage.dao;
......
public class ProductDaoJpa extends BaseDaoJpa implements ProductDao {
public Product getProduct(Integer id) {
return (Product) getJpaTemplate().find(Product.class, id);;
}
......
}
|
2.3.分析<o:p></o:p>
优点:
* 获得了分层的最基本的好处。
* 易于理解,快速掌握:没有采用更复杂的技术,并且有丰富的示例资源。
* 有广泛的工具支持,非常容易的从Spring/Hibernate类型的框架获益。
* 适合于模型简单,以CRUD操作为主的领域。
缺点:
* 模型的领域表达能力欠缺。
* 代码职责分配不合理。
3. 丰富领域模型<o:p></o:p>
3.1分层模式<o:p></o:p>
典型的DDD风格的分层模式如下:
层
|
任务
|
对象
|
描述
|
表现层
|
表现逻辑
|
模型对象
|
领域层中的实体/值对象,也可以用独立的对象。
|
视图
|
<o:p> </o:p>
|
控制器
|
<o:p> </o:p>
|
应用层
|
应用逻辑
|
服务
|
对用例建模
|
领域层
|
领域逻辑
|
服务
|
对领域操作建模
|
实体
|
对领域概念建模、并可被持久化
|
值对象
|
Value Object。对领域概念建模
|
存储库
|
Repository。隔离持久化
|
基础设施
|
持久化逻辑和基础服务
|
数据映射
|
通常以DAO模式封装持久化操作
|
基础服务
|
|
可以参考下面的示意图:[见附件2]
在实践中,我个人更倾向于将数据映射从基础设施层分离出来,这样会有一个更清晰的层次结构,如下图所示:[见附件3]
无论哪种领域模型,在是否将领域对象传递到表现层作为模型数据的问题上一直存在争议。要做出恰当的设计决定,需要在灵活性和严谨性之间做出平衡。有一些技术上的方法可以使领域对象在作为DTO对象进行传递时得到保护,可以参考下面的文章《Protecting the Domain Model》:
http://api.blogs.com/the_catch_blog/2005/05/protecting_the_.html
这些做法在一定程度上弥补了直接传递领域对象带来的负面影响。
3.2有什么不同?<o:p></o:p>
从上面的表格我们可以发现,要做的事还是那些事,只不过部分职责被重新分配了而已。相对于贫血领域模型,在新的分层模式中:
* 不变的是表现层;
* 被合并的是数据源层,现在成了基础设施的一部分;
* 增加的是应用层;
* 被重新组织的是领域层,其职责被分配到相应的对象中:实体、值对象、服务和存储库。
这里最大的变化有三个:
1)应用逻辑被从领域逻辑中独立出来,形成了新的应用层。该层的接口按照用例进行设计,因此粒度较大;该层反映的是用系统所实现的任务,如果需要,也能反映工作流程。
2)领域层只反映领域逻辑中最核心部分,因此也是最复杂的(如果领域复杂程度超过技术复杂程度的话)。其中的实体不但持有数据,还具备丰富的行为;服务是一些领域相关的操作(这不同于应用层的服务,更不同于基础设施层的服务);通过存储库隔离了与数据技术的联系。
3)领域层可以独立地访问其他层的服务和资源。这是一个有争议的话题,同时很有技术上的挑战性,下文会通过实例代码进行说明。
- 描述: 贫血领域模型示意图
- 大小: 16.6 KB
- 描述: 丰富领域模型示意图
- 大小: 22 KB
- 描述: 丰富领域模型变体示意图
- 大小: 23.4 KB
分享到:
相关推荐
第1章 初步了解DDD 课程介绍 抛开杂念,看看传统三层CRUD编程方式 DDD领域驱动设计到底是什么? DDD和传统三层优劣势比较 DDD在国内现象是个什么情况? DDD从战略设计到战术设计概览 第2章 领域分析模型 核心域,...
- 第一天主要介绍DDD设计篇,通过事件风暴法进行业务领域建模,建立统一语言建模,理解成为顶级业务架构师的本质。 - 第二天关注DDD实践篇,讲解如何基于领域模型进行数据库和程序设计,以及如何设计聚合、工厂和...
例如,某地区有 10% 的黑人是镰状网性贫血症隐性患者,如果当地政府采取控制结合措施,到下一代的隐性患者将减少到 5% ;而且只要进行四代的控制,隐性患者所占百分比不到 1% 。 思考: (1)基因分布表给出的概率...
8. **贫血模型与领域模型** - 讨论了一个简单的示例,比较了贫血模型(Anemic Domain Model)和领域模型(Domain-Driven Design)在Java应用中的应用和优缺点。 9. **Spring中的DAO与Service** - 在Spring框架中,...
标题中的“PyTorch-YOLOv3-master”指的是一个基于PyTorch实现的YOLOv3模型的源代码库。YOLO(You Only Look Once)是一种流行的实时目标检测算法,由Joseph Redmon等人在2016年提出。YOLOv3是其第三版,相较于前两...
这篇文档是针对初中科学课程,特别是八年级下册第二章“微粒的模型与符号”的测试题,旨在考察学生对化学基础知识的理解,包括化学式、元素符号、分子结构以及原子间的关系。以下是对题目中涉及知识点的详细解释: ...
传统的领域类(domain classes)往往是贫血模型,缺乏面向对象或领域驱动的设计。引入Spring和JPA的结合使用,可以极大地简化这一过程,让开发者更专注于富有领域模型特性的持久层的实现。 Spring Data JPA的仓库...
模型建立涉及到了患者和正常人的离差矩阵、协方差矩阵,以及马氏距离的概念,这些都是判别分析中的重要组成部分。Fisher判别模型的构建还包括了寻找判别函数的过程,以最大化两类样本间的可分性。 总之,本文详细...
EJB的体系结构、编程模型和历史演变(如在"转:第一部分:EJB 体系结构的历史和目标.txt"中描述的)是深入理解企业级Java开发的关键。 6. **XP(Extreme Programming)实践**:"XP 精华----如何使 Java 项目获得更...
10. **生物变异来源**:图10中,镰刀型细胞贫血症由基因突变引起,⑤过程发生在减数第一次分裂,①和②分别代表基因突变和基因重组,结构变异不一定会改变生物性状。 11. **遗传疾病分析**:图11中,甲图可能表示...
18. **贫血模型与充血模型**:对比两种不同设计模式的优缺点,探讨最佳实践。 19. **课程总结**:回顾整个课程,强调掌握QFramework System Design Architecture对架构设计的重要性。 课程通过实例教学,逐步揭示...
【知识点解析】 1. 生物分类:题目中提到的“真核生物”是指拥有细胞核...以上是对高一生物第一学期期末质量调研测试部分知识点的解析,涵盖了生物分类、细胞结构与功能、代谢过程、显微镜使用、实验操作等多个方面。
图中有4条染色体,2对同源染色体,处于减数第一次分裂的中期,A与C的分离发生在减数第一次分裂后期。 6. 生命科学史:沃森和克里克提出DNA双螺旋模型后,随即提出了半保留复制的假设。早期普遍认为蛋白质是遗传物质...
- **贫血/富模型**:根据业务场景选择合适的模型设计,提升数据访问的灵活性和安全性。 5. **测试与调试** - **单元测试**:使用JUnit等工具对单个功能模块进行测试。 - **集成测试**:检查不同模块间的协作是否...
- **POJO对象**:与数据库表对应的Java类,通常遵循贫血模型,只包含数据属性。 - **映射文件**:定义了类与表的映射关系,包括字段映射、主键生成策略等。 - **应用程序**:通过Hibernate API与数据库交互,如`...
《Expert One-on-One J2EE Design》是一本深入探讨Java企业版(J2EE)设计策略的专业书籍。这本书由行业专家撰写,旨在帮助...书中的内容覆盖了从基础概念到高级主题的广泛领域,是J2EE开发者和架构师的宝贵资源。
2. **遗传信息的传递**:遗传信息从DNA传递到mRNA,再到蛋白质,形成基因→mRNA→蛋白质的中心法则。在这个过程中,密码子是mRNA上的三个核苷酸序列,对应一个氨基酸,决定了蛋白质的氨基酸序列。 3. **DNA与RNA...
【知识点】 1. 内环境与稳态:内环境包括血液、组织液和淋巴,是体内细胞...以上知识点涵盖了生物体内环境稳态、免疫系统、神经系统、植物激素、种群生态学、免疫接种、种群增长模型和植物生长调节等多个生物学领域。