应用程序框架实战二十一:DDD分层架构之仓储(介绍篇)
前面已经介绍过Entity Framework的工作单元和映射层超类型的封装,从本文开始,将逐步介绍仓储以及对查询的扩展支持。
什么是仓储
仓储表示聚合的集合。
仓储所表现出来的集合外观,仅仅是一种模拟,除了测试以外,没有理由使用内存中真正的集合来创建仓储。
不应该为所有实体建立仓储,只有聚合才拥有仓储。
仓储用来重建已持久化的聚合,而工厂用于新建聚合。
使用仓储的优点
直接使用Entity Framework的DbContext不是很好吗,为什么还要在DbContext的上方封装一层仓储呢,这是否多此一举?
很多使用EF的程序员确实是直接使用DbContext,并且他们发现开发起来十分简单,因为DbContext的接口设计得非常优雅,从接口上看,DbContext就好像所有实体集合的仓库。
另一方面,很多使用了仓储的朋友,都是依葫芦画瓢,虽然创建了仓储,但并没有体会到多大好处。
下面简要介绍使用仓储将为你带来的优点。
从概念上简化数据操作
仓储模拟了某种聚合的集合,而DbContext包含了N种类型的集合。
与仓储相近的一个数据访问模式是数据访问对象(DAO)模式,很多人认为仓储不过是数据访问对象换了一个名词而已,从技术上说的确如此,仓储的强大之处在于概念上更简单。仓储在概念上代表内存中的集合操作,而数据访问对象代表数据库操作,很明显,集合比数据库在概念上更简单。
减少冗余查询逻辑
如果直接使用DbContext,由于特定查询逻辑没有一个固定位置,可能分散到任何地方,这很容易造成冗余。
仓储是封装特定查询逻辑的好地方,对于特定的查询逻辑,放到与聚合相应的仓储中即可。
降低耦合度
直接使用DbContext,所有调用代码与EF实现高度耦合。
另一方面,由于DbContext能够获取任意实体,这些实体可能位于聚合内部,这样会破坏聚合的封装性,同时在任意位置可以获取任意对象,由于缺乏约束力而导致更高的耦合。
方便单元测试
直接使用DbContext只能进行集成测试,必须连接到真实数据库。而领域层只持有仓储接口,所以测试时很容易替换成模拟实现,从而避开数据库。
使用仓储的要点
使用更具体的仓储
一般来讲,ICustomerRepository比直接使用泛型的IRepository<Customer>要好。
为了定义通用操作,我们会创建一些泛型基类来实现基础操作。
有些人发现很多具体仓储只是直接继承泛型仓储,比如ICustomerRepository和CustomerRepository从泛型的IRepository<Customer>和Repository<Customer>派生,但CustomerRepository本身并没有其它什么代码。于是很多人学会偷懒,直接使用泛型基类进行操作。
ICustomerRepository优于IRepository<Customer>的原因如下。
1. 概念上更清晰。
ICustomerRepository从概念上良好表达了该仓储用于操作客户,而泛型仓储可以操作任何聚合。
2. 为特定查询逻辑提供唯一封装和访问点。
ICustomerRepository可以将客户相关的各种复杂查询封装起来,而IRepository<Customer>很难对客户查询进行封装,一个办法是使用扩展方法来完成封装,不过比起CustomerRepository要麻烦得多,而且实现代码也更难管理。
3. 降低耦合。
泛型IRepository<>可以在任意位置获取任意聚合,这比起DbContext要强一些,不会破坏聚合的封装性,但缺乏约束力仍然会导致更高耦合。
ICustomerRepository通过明确的声明,只能获取需要的聚合,从而控制了对象的访问。
仓储仅返回与其聚合高度相关的内容
仓储可以返回对应的聚合,也可以返回聚合相关的统计信息,甚至可以返回聚合的一个子集。
有人在仓储查询中使用匿名对象,使用匿名对象的原因很简单,即为了进行投影操作,这样可以限制Sql查询返回的列,对于Sql性能调优来讲,这可能非常重要,比如使用Sql Server的覆盖索引。
但是使用匿名对象进行查询有很多问题,一个问题是无法直接作为结果返回。有人为了省力,直接使用聚合或其部分子对象作为返回类型,其结果是对象中的一部分属性被赋值,另一部分属性为null。这可能导致许多神秘的Bug,另外导致你或其它人对仓储查询信心不足,因为这些查询并不安全,你每当需要调用一个查询,首先得仔细检查代码,看看哪些值是空的,以免踩到地雷。
如果要返回聚合的一个子集,需要单独定义一些对象,以作为返回类型。工作量虽然比较大,但更加安全和清晰。
如果需要获取的内容由多个聚合组成,这个查询操作应该放入哪个仓储中?这种情况放到哪个仓储其实都不合适,更好的办法是使用一个查询服务,就好像跨越多个聚合的业务操作需要领域服务一样。
总结
1. 仓储的定义
仓储表示聚合的集合。
2. 仓储的优点
- 简化数据操作
- 减少冗余查询逻辑
- 降低耦合度
- 方便单元测试
3. 仓储的要点
- 使用更具体的仓储
- 仓储查询仅返回与其聚合高度相关的内容
由于仓储是对数据操作的封装,包括仓储基础,分页,查询扩展,查询对象,查询条件(规约模式),查询条件应用(日期范围与数值范围查询)等内容,所以需要多篇文章进行介绍。
.Net应用程序框架交流QQ群: 386092459,欢迎有兴趣的朋友加入讨论。
谢谢大家的持续关注,我的博客地址:http://www.cnblogs.com/xiadao521/
应用程序框架实战二十一:DDD分层架构之仓储(介绍篇)的更多相关文章
- 应用程序框架实战十五:DDD分层架构之领域实体(验证篇)
在应用程序框架实战十四:DDD分层架构之领域实体(基础篇)一文中,我介绍了领域实体的基础,包括标识.相等性比较.输出实体状态等.本文将介绍领域实体的一个核心内容——验证,它是应用程序健壮性的基石.为了 ...
- 应用程序框架实战十八:DDD分层架构之聚合
前面已经介绍了DDD分层架构的实体和值对象,本文将介绍聚合以及与其高度相关的并发主题. 我在之前已经说过,初学者第一步需要将业务逻辑尽量放到实体或值对象中,给实体“充血”,这样可以让业务逻辑高度内聚, ...
- 应用程序框架实战十六:DDD分层架构之值对象(介绍篇)
前面介绍了DDD分层架构的实体,并完成了实体层超类型的开发,同时提供了验证方面的支持.本篇将介绍另一个重要的构造块——值对象,它是聚合中的主要成分. 如果说你已经在使用DDD分层架构,但你却从来没有使 ...
- 应用程序框架实战十四:DDD分层架构之领域实体(基础篇)
上一篇,我介绍了自己在DDD分层架构方面的一些感想,本文开始介绍领域层的实体,代码主要参考自<领域驱动设计C#2008实现>,另外参考了网上找到的一些示例代码. 什么是实体 由标识来区分的 ...
- DDD分层架构之仓储
DDD分层架构之仓储(层超类型基础篇) 前一篇介绍了仓储的基本概念,并谈了我对仓储的一些认识,本文将实现仓储的基本功能. 仓储代表聚合在内存中的集合,所以仓储的接口需要模拟得像一个集合.仓储中有很多操 ...
- 应用程序框架实战二十二 : DDD分层架构之仓储(层超类型基础篇)
前一篇介绍了仓储的基本概念,并谈了我对仓储的一些认识,本文将实现仓储的基本功能. 仓储代表聚合在内存中的集合,所以仓储的接口需要模拟得像一个集合.仓储中有很多操作都是可以通用的,可以把这部分操作抽取到 ...
- 【WePY小程序框架实战二】-页面结构
[WePY小程序框架实战一]-创建项目 项目结构 |-- dist |-- node_modules |-- src | |-- components |-- a.wpy |-- b.wpy |-- ...
- 应用程序框架实战二十九:Util Demo介绍
上文介绍了我选择EasyUi作为前端框架的原因,并发放了最新Demo.本文将对这个Demo进行一些介绍,以方便你能够顺利运行起来. 这个Demo运行起来以后,是EasyUi的一个简单CRUD操作,数据 ...
- DDD分层架构之领域实体(验证篇)
DDD分层架构之领域实体(验证篇) 在应用程序框架实战十四:DDD分层架构之领域实体(基础篇)一文中,我介绍了领域实体的基础,包括标识.相等性比较.输出实体状态等.本文将介绍领域实体的一个核心内容—— ...
随机推荐
- 关于C#开发WEB项目TextBox控件的自适应高问题解决办法!
前两天做WEB开发时候遇到了一个小问题TextBox 控件要根据输入的内容多少自动改变大小,并且这个大小要求是在本页面内最大化.也就是 Width="100%" Height=&q ...
- Cannot attach the file ‘{0}' as database '{1}'
EF使用CodeFirst,当使用localDB时,删除mdf文件,会报“Cannot attach the file ‘{0}' as database '{1}'”错误. 解决方法如下: 1.打开 ...
- C++ activemq CMS 学习笔记.
很早前就仓促的接触过activemq,但当时太赶时间.后面发现activemq 需要了解的东西实在是太多了. 关于activemq 一直想起一遍文章.但也一直缺少自己的见解.或许是网上这些文章太多了. ...
- sql server 代理服务
sql server 创建维护计划失败.错误代码:c001f011. 从 IClassFactory 为 CLSID 为 {17BCA6E8-A95D-497E-B2F9-AF6AA475916F} ...
- 怎么将java web 项目导入idea 中
1.将 java web 项目导 入idea 中, 显示 然后进行 Configure 配置. 2. 点击 open module settings. 3. 4. 选择jar包. 5. 6. 配置to ...
- linux一句话轻松提权
linux命令: [b@fuckks~]$ printf "install uprobes /bin/sh" > exploit.conf; MODPROBE_OPTI** ...
- vs2012 打开解决方案崩溃或者点击项目崩溃
报错: 问题签名: 问题事件名称: CLR20r3 解决方案: 步骤1:开始-->所有程序-->Microsoft Visual Studio 2012-->Visual Stud ...
- HDU3465 树状数组逆序数
Life is a Line Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 131072/65536 K (Java/Others)T ...
- 关于mySQL自连接的一些用法
自连接是连接的一种用法,但并不是连接的一种类型,因为他的本质是把一张表当成两张表来使用. 举例说明: 这是一张职员信息表,如果我要查询这张表中的每个职员的上司,那么必须使用自连接来查询.所以为了能实现 ...
- 项目 "Model" 受源代码管理。向源代码管理注册此项目时出错。建议不要对此项目进行任何更改。