MVC Web 应用程序的域驱动设计与数据库驱动设计

Posted

技术标签:

【中文标题】MVC Web 应用程序的域驱动设计与数据库驱动设计【英文标题】:Domain Driven Design vs Database Driven Design for an MVC Web application 【发布时间】:2012-05-27 22:19:23 【问题描述】:

我正在将旧版 Web 窗体应用程序扩展/转换为全新的 MVC 应用程序。扩展既是技术方面的,也是业务用例方面的。遗留应用程序是一个完善的数据库驱动设计 (DBDD)。所以例如如果您有不同类型的员工,如操作员、主管、商店管理员等,并且您需要添加新类型,您只需在几个表中添加一些行,瞧,您的 UI 会自动添加/更新新类型员工类型。 然而,层的分离不是那么好。

新项目有两个主要目标

可扩展性(针对当前和未来的管道要求) 性能

我打算创建一个新项目,用域驱动设计 (DDD) 替换数据库驱动设计 (DBDD),同时牢记可扩展性要求。但是,如果我将其与传统 DBDD 应用程序的性能进行比较,从数据库驱动设计转向领域驱动设计似乎会对性能要求产生负面影响。在遗留应用程序中,任何来自 UI 的数据调用都将直接与数据库交互,并且任何数据都将以 DataReader 或(在某些情况下)DataSet 的形式返回。

现在有了严格的 DDD,任何数据调用都将通过业务层和数据访问层进行路由。这意味着每次调用都会初始化一个业务对象和一个数据访问对象。单个 UI 页面可能需要不同类型的数据,这是一个 Web 应用程序,每个页面都可以由多个用户请求。此外,一个 MVC Web 应用程序是无状态的,每个请求都需要每次都初始化业务对象和数据访问对象。 因此,对于 MVC 无状态应用程序而言,DBDD 似乎比 DDD 性能更可取。

或者在 DDD 中有一种方法可以同时实现 DDD 提供的可扩展性和 DBDD 提供的性能?

【问题讨论】:

作为一个有趣的问题加注星标,因为它从字面上理解了各种设计背后的机制。很多时候,这些讨论太抽象而无用。这个很直接。我很想知道答案是什么(我自己也不知道)。 在开始思考之前,我有几个问题: 1. 性能要求到底是什么?应用程序的响应速度。是否应该在 1 秒内响应所有查询,或者在 0.5 秒内获取数据并在 1 秒内响应更新? 2. 您是否已经有一些针对当前应用程序的指标以及基于 MVC 的应用程序的运行速度会慢多少? 我有当前应用程序数据库操作的指标。除了可能具有复杂和数据繁重的操作并且可能需要几分钟的报告之外,CRUD 需要不到一秒的时间,而数据库级别的最大数据获取操作发生在 2 - 3 秒内。 MVC 应用程序会慢多少,问题都是关于 这是与无状态的 MVC 应用程序的通信。您可以(并且应该)缓存任何可以缓存的内容。 【参考方案1】:

您是否考虑过某种形式的命令查询分离,其中更新通过域模型但读取来自 DataReaders?成熟的 DDD 并不总是合适的。

【讨论】:

+ 1 用于推荐 CQRS。虽然我不得不说使用 CQRS 并不意味着您不再使用“成熟的 DDD”。恰恰相反。从您的域聚合中投影 DTO/视图模型,而不是直接从数据库中投影它们,这不会降低您正在练习的 DDD 的水平。做前者只会让你自己的生活变得艰难,并且需要一个映射层,这些对象是(或应该)根据它们在事务中强制执行的不变量设计的。对我来说,后者只是实用/明智的方式。 这不能解决批量写入查询。而且它也不能解决有时您只需要使用 ID,并且不需要对所有内容进行水合(为了性能)的问题。您可以向每个实体添加规则,但您一定会获得写入操作,这些操作将涵盖写入与多个实体相关的数据(无论它们是否是聚合),并且您需要复制这些规则,因为您是避免遍历每个实体以单独应用其规则(违背 DDD 的目的)。 这是一个很好的article,可以解决我提到的问题。我不知道给出的解决方案是否足以解决问题。这是一个艰难的课题。 CQRS 有帮助,但我们仍然需要处理写入方面的性能问题。【参考方案2】:

在您的方案中使用 DDD 不会对性能产生重大影响。您担心的似乎更像是数据访问问题。你把它称为

初始化一个业务对象和一个数据访问对象

为什么“初始化”很昂贵?您使用什么数据访问机制?

具有存储在关系数据库中的长寿命对象的 DDD 通常使用 ORM 实现。 如果使用得当,ORM 对大多数应用程序的性能影响很小(如果有的话)。如果存在已证实的瓶颈,您始终可以将应用程序中对性能最敏感的部分切换回原始 SQL。

不管怎样,NHibernate 只需要在应用程序启动时初始化一次,然后它使用与常规数据读取器相同的 ADO.NET 连接池。因此,这一切都归结为正确的映射、获取策略和避免典型的数据访问错误,如“n+1 选择”。

【讨论】:

【参考方案3】:

“现在有了严格的 DDD,任何数据调用都将通过业务层和数据访问层进行路由。”

我不相信这是真的,而且肯定不切实际。我相信这应该是:

现在有了严格的 DDD,任何对事务的调用都将通过业务层和数据访问层进行路由。

没有什么说您不能直接调用数据访问层来获取您需要在屏幕上显示的任何数据。只有当您需要修改数据时,您才需要调用基于其行为设计的域模型。在我看来,这是一个关键的区别。如果您通过域模型路由所有内容,您将遇到三个问题:

    时间 - 实现功能会花费您更长的时间,而且没有任何好处。 模型设计 - 您的域模型将变形,以满足查询而不是行为的需求。 性能 - 不是因为额外的层,而是因为您无法像直接从查询中那样快速地从模型中获取聚合数据。即,考虑为特定客户下达的所有订单的总价值 - 为此编写查询比获取客户的所有订单实体、迭代和求和要快得多。

正如 Chriseyre2000 所提到的,CQRS 旨在解决这些确切的问题。

【讨论】:

以上是关于MVC Web 应用程序的域驱动设计与数据库驱动设计的主要内容,如果未能解决你的问题,请参考以下文章

在域驱动设计中使用外部 Web 服务

Spring MVC 实践 - Base

远程 API/Web 服务层 MVC 的域逻辑和数据访问

业务中台与领域驱动架构设计

Spring Web MVC入门

初步认识spring mvc