4.重构到丰富领域模型构架<o:p></o:p>
4.1.技术方案<o:p></o:p>
为了简便起见,下文中使用A和B这两个别名分别代表“贫血领域模型”和“丰富领域模型”,使用名称空间表示模型中的层,如B::应用层表示丰富领域模型的应用层。
通过前面的比较,可以很容易的得到下面转换的方案:
1)保持表现层不变
2)分离出来应用层。从A::领域层移出应用逻辑形成B::应用层。
3)重构一个纯粹的领域层。将A::领域层的领域逻辑部分进行分解:
# 将概念性的逻辑重构到B::领域层.实体和B::领域层.值对象;
# 将操作性逻辑重构到B::领域层.服务;
# 将数据源访问抽象为存储库接口,存储库的实现由Ioc容器注入。
<!----><!---->
4)重构数据访问对象,使其实现领域层的存储库接口。
5)基础设施层保持不变。
不多做解释,下面通过代码进行说明。
4.2. 示例代码<o:p></o:p>
还是前面的在线购物网站,但这次使用了一个稍微能体现领域逻辑的用例:列出与当前产品相关的其他产品。
1)页面发出请求
下面是一个可能的JSF代码片断:
- <h:commandLink action="#{product.related}" value="#{相关产品}">
- <f:param name="productid" value="#{product.id}" />
- <!---->h:commandLink>
2)代理到控制器
与前面一样,只是增加了一个响应查询相关产品的响应事件:
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>
//响应查询相关产品的事件<o:p></o:p>
public String related() {<o:p></o:p>
product = productService.getRelatedProduct(id);<o:p></o:p>
return “products”; //JSF将其解析为products.xhtml视图<o:p></o:p>
}<o:p></o:p>
<o:p> </o:p>
}
|
3)领域层的服务
与前面贫血领域模型的代码中有一些区别:用Reporsitory代替了Dao,并增加了一个获取产品信息的领域逻辑方法。
package yourpackage.service;
......
public class ProductServiceImpl extends BaseService implements ProductService {
//通过依赖注入的存储库实现
private ProductRepository productRepository;
<o:p> </o:p>
//获取产品信息的领域逻辑方法
public Product getProduct(Integer id) {
return productRepository.getProduct(id);
}
//获取产品信息的领域逻辑方法
public Product getRelatedProduct(Integer id) {
Product product= productRepository.getProduct(id);
return product.getRelated();
}
}
|
4)领域层的实体
一切魔法都发生在这里!
现在我们的实体除了封装数据外,还有了一个真正的领域方法:getRelated()。
package yourpackage.model;
<o:p> </o:p>
import org.springframework.beans.factory.annotation.Configurable;
import org.springframework.beans.factory.annotation.Autowire;
......
<o:p> </o:p>
@Entity
@Configurable(autowire=Autowire.BY_TYPE ,dependencyCheck=true)
public class Product {
<o:p> </o:p>
//通过依赖注入的存储库实现
private ProductRepository repository;
public void setProductRepository(ProductRepository repository) {
this.repository = repository;
}
<o:p> </o:p>
@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;
}
<o:p> </o:p>
//这是一个领域逻辑方法
public List
<product> getRelated() {</product>
return repository.getRelated(getId());
}
}
|
下面来解释一下魔法的背后到底发生了什么。
从Spring 2.0开始,在AspectJ的帮助下,提供了一个annotation驱动的方面用于任何对象的依赖注入,即使这些对象并不是由容器所创建。最典型的就是由Hibernate管理的领域对象。
@ Configurable是一个标记性的Annotation,告诉Spring,在实例化这个类的时候需要使用一个原型。autowire=Autowire.BY_TYPE则告诉Spring,这个原型是根据类型自动创建的(autowiring),这避免了在Spring配置文件中显式的定义一个Product类型的Bean。另外还需要通过dependencyCheck=true来告诉Spring,在新建Product类型的实例时进行依赖检查。由于我们的Product中出现了setProductRepository(),而Spring恰好发现了一个名为productRespository的Bean:
- <bean id="productRepository" class="yourpackage.dao.ProductRepositoryImpl" />
于是就将这个Bean的实例设置到Product对象的repository。这就是大家早已熟悉的依赖注入,只不过这一次发生在了领域对象的身上。
这一切之所以能够发生,是因为背后一个叫做AnnotationBeanConfigurerAspect的方面在起作用。使用该类需要引入Spring发行包中的spring-aspects.jar,并且在配置文件中进行如下定义:
最后要做的一件事是,必须有某种机制使得被注解的类能够被
AspectJ weaver织入。一种方法是用AspectJ的ajc编译器对代码进行预编译,另外一种方式是使用AspectJ Load-time weaving (LTW)。
如果在Java 5环境使用LTW,需要在类路径下的META-INF目录中编写一个名为aop.xml的文件,以告诉AspectJ对那些类进行织入处理:
aop.xml 代码
- "-//AspectJ//DTD//EN" "http://www.eclipse.org/aspectj/dtd/aspectj.dtd">
-
- <aspectj>
- <weaver>
- <include within="yourpackage.domain.Product"/>
- <!---->weaver>
- <!---->aspectj>
然后,需要在JVM加载参数中指定下面的内容:
-javaagent:
<path-to-ajlibs>/aspectjweaver.jar</path-to-ajlibs>
|
现在可以说大功告成了。
5)DAO对象
现在DAO需要实现的接口变成了领域层的ProductRepository:
package yourpackage.dao;
<o:p> </o:p>
import yourpackage.domain;
......
public class ProductDaoJpa extends BaseDaoJpa implements ProductRepository {
public Product getProduct(Integer id) {
return (Product) getJpaTemplate().find(Product.class, id);;
}
<o:p> </o:p>
public Product getRelatedProduct(Integer id) {
//这里是具体的实现代码
}
}
|
5.结论<o:p></o:p>
要构建一个纯粹的领域模型,往往需要在领域对象中直接使用外部服务,如数据库访问,外部资源,或其他必要的服务。在没有强大的Annotation、AOP及DI技术的支持下,这些外部服务或资源很难被注入到领域对象中,由此形成了贫血模型的设计风格。
特别地,对于Spring用户来说,在2.0版发布之后,问题的解决变得更容易一些。利用Spring集成的AspectJ实现向领域对象进行依赖注入,可以使领域对象可以表达更丰富的逻辑,从而过渡到丰富领域模型。在这个关键问题解决之后,剩下的问题也就迎刃而解了。
分享到:
相关推荐
第2章 领域分析模型 核心域,支撑子域,通用子域 微服务和DDD是什么关系? 传统模式下如何合理的划分各种域 基于DDD的方式进行域划分 什么是通用语言 什么是限界上下文? 限界上下文和子域的关系 基于电商系统按流程...
- 第二天关注DDD实践篇,讲解如何基于领域模型进行数据库和程序设计,以及如何设计聚合、工厂和仓库。 - 第三天聚焦DDD架构篇,讨论如何构建支持领域驱动设计的技术中台和微服务架构,以及通过整洁架构支持技术架构...
例如,某地区有 10% 的黑人是镰状网性贫血症隐性患者,如果当地政府采取控制结合措施,到下一代的隐性患者将减少到 5% ;而且只要进行四代的控制,隐性患者所占百分比不到 1% 。 思考: (1)基因分布表给出的概率...
【JavaEye论坛热点月报 总第7期】 在这一期的JavaEye论坛...这些热点话题反映了当时JavaEye论坛上最活跃的技术讨论,涵盖从基础框架到高级工具,再到具体应用实践的广泛领域,为开发者提供了丰富的学习和交流资源。
这篇文档是针对初中科学课程,特别是八年级下册第二章“微粒的模型与符号”的测试题,旨在考察学生对化学基础知识的理解,包括化学式、元素符号、分子结构以及原子间的关系。以下是对题目中涉及知识点的详细解释: ...
6. 应用部署:将训练好的模型集成到实际应用中,如医疗诊断系统,实现自动化血细胞检测。 标签“计算机视觉 yolov3”强调了这个项目属于计算机视觉领域,使用了YOLOv3技术。计算机视觉是AI的一个分支,专注于让机器...
最后,对比第二问和第四问的结果,进行深入分析。 在建模过程中,假设了数据的准确性,其他微量元素的影响较小,且外界条件对肾炎的影响可忽略。模型建立涉及到了患者和正常人的离差矩阵、协方差矩阵,以及马氏距离...
传统的领域类(domain classes)往往是贫血模型,缺乏面向对象或领域驱动的设计。引入Spring和JPA的结合使用,可以极大地简化这一过程,让开发者更专注于富有领域模型特性的持久层的实现。 Spring Data JPA的仓库...
6. 元素符号书写:正确的元素符号如硫(S),第一个字母大写,第二个字母小写。 7. 元素符号一致性:符号书写正确且与名称相一致的是硫(S)。 8. 元素符号首字母:氯、碳、钙、铜的首字母都是"C",属于同一组。 ...
4. **设计模式**:在"实体对象的抽象以及一种基于数据库的实现(转).txt"中可能涉及到贫血模型和富模型两种常见的对象关系映射设计模式。贫血模型的对象只包含数据,而业务逻辑存在于服务层;富模型则让对象包含数据...
5. **化学反应微观示意图**:题目展示了反应的微观模型,涉及到了化合反应,分子和原子的种类在反应前后保持不变,以及分子数量的变化。 6. **化学用语**:正确理解化学符号是关键,例如2MnO42﹣表示两个锰酸根离子...
18. **贫血模型与充血模型**:对比两种不同设计模式的优缺点,探讨最佳实践。 19. **课程总结**:回顾整个课程,强调掌握QFramework System Design Architecture对架构设计的重要性。 课程通过实例教学,逐步揭示...
在设计上,可能采用贫血模型或充血模型,根据业务需求选择合适的对象状态管理方式。 数据库设计是项目的关键部分,可能包括用户表、商品表、订单表、购物车表等,需要考虑到数据的一致性、安全性和性能。例如,用户...
- **POJO对象**:与数据库表对应的Java类,通常遵循贫血模型,只包含数据属性。 - **映射文件**:定义了类与表的映射关系,包括字段映射、主键生成策略等。 - **应用程序**:通过Hibernate API与数据库交互,如`...
2. **设计原则**:强调了面向服务架构(SOA)和分层架构的重要性,讨论了如何根据业务需求选择合适的设计模式,例如贫血模型和富模型,以及何时使用无状态会话Bean和有状态会话Bean。 3. **性能优化**:探讨了J2EE...
5. 免疫防御:中性粒细胞吞噬是第二道防线;血浆蛋白破坏病原体非第一道防线;皮肤阻挡细菌属于第一道防线;对外来器官的排异反应是免疫系统的第三道防线。 6. 神经元间传递:兴奋可通过突触传递;④神经递质释放是...
- **贫血/富模型**:根据业务场景选择合适的模型设计,提升数据访问的灵活性和安全性。 5. **测试与调试** - **单元测试**:使用JUnit等工具对单个功能模块进行测试。 - **集成测试**:检查不同模块间的协作是否...
2. 糖类的比较:淀粉、纤维素和糖原都是多糖,由葡萄糖单体组成,但结构差异导致它们的功能不同。它们的共同点在于基本组成单位都是葡萄糖(六碳糖),而非六碳糖;不含有氮元素(N);并非所有都是细胞内储存能量的...