文章首发到公众号:月伴飞鱼
文章内容收录到个人网站,方便阅读:hardyfish.top/
文章内容收录到个人网站,方便阅读:hardyfish.top/
配合以下文章一起看:
❝
仓储就类似于仓库管理员,它是聚合的管理。
仓储介于领域模型和数据模型之间:
- 主要用于聚合的持久化和检索。
它隔离了领域模型和数据模型,以便我们关注于领域模型而不需要考虑如何进行持久化。
为什么要用仓储
解耦领域层和基础层
DDD严格的分层架构告诉我们:
❝
每一层只能与其下方的一层发生耦合。
因此用户接口层只与应用层发生交互,应用层往下只与领域层发生交互,领域层往下只与基础层发生交互。
在传统的代码分层结构Controller—Service—Dao
结构中:
❝
经常能看到在
Service
业务实现层的代码中嵌入SQL
,或者在其中频繁出现修改数据对象并调用DAO
的情况。
- 这样的话,基础层的数据处理逻辑就渗透到了业务逻辑代码中。
在DDD
的分层结构中:
❝
如果出现上述情况,则基础层的数据处理逻辑就渗透到了领域层。
- 领域层中的领域模型就难以聚焦在业务逻辑上,对外层的基础层产生了依赖。
而一旦涉及到数据逻辑的修改,就要到领域层中去修改代码。
本文要讲的仓储模式就是用来解耦领域层和基础层的,降低他们之间的耦合和相互影响。
仓储模式
仓储模式包含仓储接口和仓储实现:
❝
仓储接口
- 面向领域层提供基础层数据处理相关的接口。
仓储实现
- 完成仓储接口对应的数据持久化相关的逻辑处理。
一个聚合配备一个仓储,由仓储完成聚合数据的持久化。
- 领域层逻辑面向仓储接口编程,聚合内的数据持久化过程为
DO
(领域对象)转PO
(持久化对象)。
当需要更换数据库类型,或者更改数据处理逻辑时:
❝
我们就可以保持业务逻辑接口不动,只修改仓储实现,保证了领域层业务逻辑和基础层逻辑隔离。
仓储的架构
仓储要依赖数据库、内存等具体的实现工具去做真正的持久化。
如下图所示(图中连线代表依赖关系):
我们可以把仓储的行为抽象为基本的接口,然后利用控制反转。
- 把实现该节点的仓储注入领域模型的运行态中。
实现了倒置依赖的依赖图如下:
实现举例
如下示例为一个订单聚合中对订单实体的仓储模式实现。
订单DO
定义:
public class OrderDO {
private long id;
private long orderTime;
}
订单PO
定义:
public class OrderPO {
private long id;
private long orderTime;
}
仓储接口定义:
public interface OrderRepository {
void addOrder(OrderPO order);
void updateOrder(OrderPO order);
OrderPO findById(long id);
}
仓储接口实现:
public class OrderRepositoryImpl implements OrderRepository {
@Resource
private OrderDao orderDao;
@Override
public void addOrder(OrderPO order) {
orderDao.addOrder(order);
}
@Override
public void updateOrder(OrderPO order) {
orderDao.updateOrder(order);
}
@Override
public OrderPO findById(long id) {
return orderDao.findById(id);
}
}
订单领域服务实现:
❝
后面基础层发生了变化,则领域层无需动任何代码。
- 只要仓储接口不变,领域层的逻辑就可以一直保持不变,维护了领域层的稳定性。
public class OrderDomainService {
@Resource
private OrderRepository orderRepository;
public void addOrder(OrderPO order) {
orderRepository.addOrder(order);
}
}
Respository(仓储)与DAO(数据访问层)的区别
之前一篇文章介绍过聚合:领域设计之理解聚合与聚合根!
在理解了聚合之后,我们可以知道:
❝
DAO
是技术手段,Respository
是抽象方式。
DAO
只是针对对象的操作,而Respository
是针对 聚合 的操作。
DAO
的操作方式如下:
❝
订单和和订单明细都有一个对应的
DAO
。订单和订单明细的关系并没有在对象之间得到体现。
@Service
@Transactional
public class OrderService {
public void createOrder(Order order, List orderDetailList) throws Exception {
Long orderId = orderDao.save(order);
for(OrderDetail detail : orderDetailList) {
detail.setOrderId(orderId);
orderDetailDao.save(detail);
}
}
}
Respository
的操作方式如下:
public class Order {
List orderDetail;
...
}
@Service
@Transactional
public class OrderService {
public void createOrder(Order order) throws Exception {
orderRespository.save(order);
}
}
StackOverFlow
中有一个回答,讲的很好:
❝
工厂模式
DO
对象创建时,需要确保聚合根和它依赖的对象同时被创建。
❝
如果这项工作交给聚合根来实现,则聚合根的构造函数将变得异常庞大。
所以把通用的初始化DO
的逻辑,放到工厂中去实现。
❝
通过工厂模式封装聚合内复杂对象的创建过程,完成聚合根,实体和值对象的创建。
DO
对象创建时,通过仓储从数据库中获取PO对象,通过工厂完成PO到DO的转换。工厂中还可以包含DO到
PO
对象的转换过程,方便完成数据的持久化。
public class OrderFactory {
protected Order createOrder(OrderPO orderPO){
Order order = new Order();
order.setId(orderPO.getId());
order.setOrderTime(orderPO.getOrderTime());
return order;
}
protected OrderPO createOrderPO(Order order){
OrderPO orderPO = new OrderPO();
orderPO.setId(order.getId());
orderPO.setOrderTime(order.getOrderTime());
return orderPO;
}
}
参考资料:
- 《基于DDD和微服务的中台架构与实现》
- 《架构真经》
- 《领域驱动设计:软件核心复杂性应对之道》
- 《实现领域驱动设计》
1、本站所有资源均从互联网上收集整理而来,仅供学习交流之用,因此不包含技术服务请大家谅解!
2、本站不提供任何实质性的付费和支付资源,所有需要积分下载的资源均为网站运营赞助费用或者线下劳务费用!
3、本站所有资源仅用于学习及研究使用,您必须在下载后的24小时内删除所下载资源,切勿用于商业用途,否则由此引发的法律纠纷及连带责任本站和发布者概不承担!
4、本站站内提供的所有可下载资源,本站保证未做任何负面改动(不包含修复bug和完善功能等正面优化或二次开发),但本站不保证资源的准确性、安全性和完整性,用户下载后自行斟酌,我们以交流学习为目的,并不是所有的源码都100%无错或无bug!如有链接无法下载、失效或广告,请联系客服处理!
5、本站资源除标明原创外均来自网络整理,版权归原作者或本站特约原创作者所有,如侵犯到您的合法权益,请立即告知本站,本站将及时予与删除并致以最深的歉意!
6、如果您也有好的资源或教程,您可以投稿发布,成功分享后有站币奖励和额外收入!
7、如果您喜欢该资源,请支持官方正版资源,以得到更好的正版服务!
8、请您认真阅读上述内容,注册本站用户或下载本站资源即您同意上述内容!
原文链接:https://www.shuli.cc/?p=21338,转载请注明出处。
评论0