存储过程 - 领域驱动的反模式
EntityFramework(EF)中有一项功能,就是能够根据数据库中的存储过程生成实体的行为(或称方法,以下统称方法)。我在本系列的第一篇博文中就已经提到,这种做法并不可取!因为存储过程是技术架构中的内容,而我们所关注的却是领域模型。
Andrey Yemelyanov在其“Using ADO.NET EF in DDD: A Pattern Approach”一文中,有下面这段话:
In this context, the architect also identified the anti-pattern of using the EF (ineffective use): the EF is used to mechanically derive the domain model from the database schema (database-driven approach). Therefore, the main topic of the guidelines should be the domain-driven development with the EF and the primary focus should be on how to build a conceptual model over a relational model and expose it to application programmers. |
黑体部分的意思是:(被采访的架构师)同时也指出了使用EF的一种“反模式”,即通过使用数据库结构来机械化地生成领域模型(数据库驱动的方式)。这也就证明了我在第一篇文章中指出的“只能通过领域模型来映射到数据模型,反过来则不行”的观点。
我能够理解很多做系统的朋友喜欢考虑数据库方面的事情,毕竟数据存储也是软件系统中不可或缺的部分(当然,内存数据库另当别论),数据库结构设计的好坏直接影响到系统的运行效率。比如:加不加索引、如何加索引,将对查询效率造成重大影响。但请注意:你把过多的精力放在了技术架构上,而原本更重要的业务架构却被扔到了一边。
什么时候选择存储过程?我认为存储过程的操作对象应该是数据,而不是对象,业务层也不可能把业务对象交给数据库去处理。其实,存储过程本身的意义也就是将数据放在DB服务器上处理,以减少客户程序与服务器之间的网络流量和往返次数,从而提高效率。所以,可以把查询次数较多的、与业务无关的数据处理交给存储过程(比如数据统计),而不要单纯地认为存储过程能够帮你解决业务逻辑问题,那样做只会让你的领域模型变得混乱,久而久之,你将无法应对复杂的业务逻辑与需求变更。
在做技术选型的时候还要注意一点,也就是存储过程的数据库相关性。存储过程是特定于某种关系型数据库机制的,比如,SQL Server的存储过程与Oracle的存储过程并不通用,而有些数据库系统甚至不支持存储过程。因此过多使用存储过程将会给你带来一些不必要的麻烦。我个人对是否使用存储过程给出如下几点意见:1、根据需求来确定;2、不推荐使用;3、出于效率等技术考虑,需要使用的,酌情处理。
回过头来看实体框架,虽然现在支持从数据库存储过程生成实体方法的过程,但这是一种反模式,我也不希望今后EF还提供将实体方法映射到存储过程的功能,因为行为不同于数据,数据是状态,而行为是业务,它与领域密切相关,它不应该被放到“基础结构层”这一技术架构中。