.Net企业级应用架构设计之数据访问层
综述
数据访问层的设计很大程度上取决于项目干系人需求的影响。例如,数据访问层应该持久化对象模型还是简单的的值的集合?数据访问层应该支持一种数据库还是多种数据库?下面仔细分析数据访问层的常见功能需求。
数据库独立性:数据访问层是系统中唯一知道并使用连接字符串和数据表名称的地方,考虑到这些,数据访问层必须要依赖于数据库管理系统DBMS,对于外部观察者,数据访问层应该就像一个黑盒,可以插入到现有系统中,封装了为某个特定DBMS实现的读取和写入的操作。
像插件一样可以配置:通常来说,数据库独立性需要使用一套普通的、跨数据库的应用编程接口。在我们看来,实现真正数据库独立需要将数据库访问层作为一个黑盒,该黑盒提供了一个固定的接口,并从配置文件中动态读取出当前数据库访问层组件的细节。还有一种实现数据库独立性的做法是使用对象/关系映射工具(O/RM)。这时,O/RM提供一套公共的API,让你仅需要简单修改配置参数就可以切换到另一个数据库,不过,有时候项目允许你使用O/RM,有时候却不行(取决于项目干系人,都懂的,有可能是带偏见的架构师,也有可能是不懂技术的客户,还有可能是头发高耸的项目经理)。关于O/RM优劣的争论这里不做讨论也不站队。
持久化应用程序的对象模型:无论何种形式,数据访问层都必须能够持久化应用程序的数据。若必须提供对象模型,那么数据访问层要能够将模型持久化至关系型结构中。当然,这会造成那个臭名昭著“对象/关系阻抗失调”问题。关系型数据库实际上存放的数据元组,而对象模型则构造出一张对象图,因此,两个模型之间自然需要映射,这也是数据访问层的主要功能。持久化应用程序的对象模型是指将数据加载至新创建对象模型和将某个实例的内容写回数据库的能力。无论选择了什么DBMS或物理上的表结构,持久化功能都不应该收到影响。依照领域模型模式设计的对象模型并不了解数据访问层的存在,不过若对象模型属于活动记录,那么数据访问层内嵌在实现模型的所有框架中。
数据访问层的职责
数据访问层对使用者来说有四个职责。首先,数据访问层需要将数据持久化至物理存储中,并为外部世界提供CRUD服务。其次,数据访问层还要处理其接收的所有数据相关的请求,数据访问层必须满足事务性需求。最后,数据访问层也要合理的处理并发。从概念角度,数据访问层可以看作是封装了4中服务的黑箱。
若开发团队在项目中自行创建数据访问层,那么就必须收工实现这些职责并头从编写代码。若可以选用O/RM工具,让工具完成一些职责。
数据访问层和其他层
数据访问层和业务层:若你倾向于相对简单的领域逻辑,那么数据访问层仅仅包含表适配器,用来实用查询从DBMS中获取数据,并使用存储过程或SQL命令将数据写回数据库。而对于事务脚本模式,为了满足业务逻辑,数据访问层需要实现的仅仅是纯粹的CRUD操作而已。在领域模型中,数据访问层属于业务层的一个补充,一般由服务层使用。
数据访问层和服务层:若你拥有领域驱动的业务逻辑,那么数据访问层就可以作为持久化模型的工具。数据访问层和领域模型并不直接通信,而是靠服务层来协调,这也是架构上的关键之处。
数据访问层和表现层:数据访问层在理论上不可能触及到表现层。在现实世界中,表现层也许会直接调用数据访问层中的一些方法,这样做的关键并不在于你能“投机取巧”的为某个问题找到了快速的解决方案,更加重要的是你必须意识带你正在“投机取巧”。经验告诉我们,若能从实际出发并恰到好处的控制此类表现层调用数据访问层的做法,那么结果非常不错,这样做也为两个不想管的层之间添加了不应有的耦合,因此在需要时可以拿来急用,不过随后要尽早重构。
数据访问层的最终形态大多取决于架构师对业务层的设计,不过在某种程度上也会有反过来的效果,即数据访问层也会影响到业务层,就像先有鸡还是先有鸡蛋这个古老的故事一样。你所受到的限制和你的关注点决定了从哪部分开始设计,也就是决定了哪一层影响哪一层。事实上,业务逻辑层设计和数据访问层设计模式之间仅有那么几种合理的组合。
设计自己的数据访问层
首先并不建议架构师和开发者从头到尾编写一个数据访问层,很多应用程序,哪怕是企业级应用也经常使用强类型DataSet或活动记录对象模型,显得太没必要,O/RM工具已经提供了所有的功能。为了确保完全不依赖于数据库结构,数据访问层和其使用者将使用业务实体(领域对象、活动记录模型中的对象、强类型DataSet或普通的数据迁移对象)来通信。这么做优势有很多,最明显的是这样的应用程序可以更容易的支持多种数据源。分离接口模式也规范了这种将数据访问层接口和实现分离开的设计原则。提取接口能够简化测试,这是个很好的理由。通常来说,分离接口模式实现了一个更加分层的设计,降低了数据访问层也其使用者之间的耦合,这样做总不是一件坏事。
是否该使用存储过程
1、传言:存储过程要比SQL代码执行效率更高:所谓的存储过程要比普通SQL有性能提升体现在对执行计划的重用上,换句话说,第一次执行存储过程时,数据库将生成执行计划,然后执行代码。下一次执行时即可重用前面已经生成的执行计划,因此效率上会有所提高。所有的SQL命令都需要执行计划。在SQLServer2005在线文档中的一段引文:“在SQL Server 2005执行任何SQL语句时,关系引擎首先查看缓存,判断其中是否有当前SQL的执行计划,SQL Server 2005将重用任何可执行的执行计划,以便减小重新编译SQL语句对性能上的影响。若没有找到现有执行的计划,SQL Server 2005才会为当前查询生成新的执行计划”。从性能角度考虑,所有到达数据库的SQL代码都会被同等对待,编译之后,二者性能没有任何差别。
2、传言:存储过程要比SQL代码安全:在执行任何SQL语句之前,数据库引擎都会尝试匹配调用者提供的认证信息和所请求资源的访问权限。根据匹配结果,引擎将决定是否执行该语句。这样看来,从安全角度存储过程显然比普通SQL代码更有优势。但是安全性是个横切的关注点,应该从表现层到数据库的各层中都有处理。今天,基于角色的安全是最灵活且最有效的做法。在基于角色的安全模型中,我们对安全性有着双重的保证,第一重是中间层中使用基于角色的安全,第二充实数据库引擎的声明式安全。若将存储过程作为整个系统的核心,那么必定会让人做出不好的设计决定。你可以使用存储过程,但不要说是出于安全性考虑,也不要用存储过程来实现逻辑。
3、传言:存储过程可用来阻挡SQL的注入攻击:存储过程当然可以降低SQL注入发生的可能,因为存储过程使用强类型参数。使用参数构造的SQL语句也可以和存储过程一样阻挡SQL的注入攻击。
4、传言:存储过程可以让SQL代码更加稳定不易改变:这是以前设计方式的产物,即数据库开发人员和其他开发人员基本没有沟通。若真想不依赖物理数据模型,那么应该选择领域驱动设计,将数据库设计成一个简单的持久化层。
小结
对领域驱动设计的需要,随之而来的是以更加概念化的方式操作数据,数据库的角色也不可避免的降低成了一个持久化工具。
补充:Web开发 , ASP.Net ,