ASP.NET MVC3 - 3层设计 - 事务控制和业务层设计问题

时间:2011-10-14 19:37:08

标签: asp.net-mvc design-patterns architecture fluent-nhibernate unit-of-work

我正在设计一个ASP.NET MVC3应用程序,我希望在3层架构中明确区分关注点。我使用Fluent NHibernate作为ORM,Repository模式与NHibernate映射的实体一起使用。我想添加一个具有Unit Of Work模式的适当业务层,保留MVC部分仅用于表示(通过使用通过业务层映射到nHibernate实体的ViewModels)。 This article很好地描述了组合的3层和MVC架构。

根据这篇MVC + unit of work + repository文章,我没有看到业务层的明显区别。工作单元类为每个存储库类型提供强类型的getter,它看起来适合业务层。但是,它暴露了一个Save方法,我认为它会转换为使用nHibernate的BeginTransaction和CommitTransaction方法。这引出了一些问题:

1)将事务控制暴露给MVC是一个好主意吗?交易控制应该在哪个阶段发生?在我看来,MVC不应该对交易负责,但如何避免这种情况?

2)是否应该有一些自动处理交易的方法? This ActionFilter implementation是半自动的,但事务控制显然位于MVC部分,而不是业务层。

3)UnitOfWork类与业务层类相同吗?
- 如果是的话,这是否意味着我们可以在其中添加自定义业务逻辑方法? - 如果没有,我们是否将工作单元包含在包含业务逻辑方法的其他类中?

我感谢任何想法或例子。谢谢。

3 个答案:

答案 0 :(得分:12)

首先,我想澄清一下关于业务层的一些误解,因为你想使用Repository模式,你的设置是Domain Driven Design的候选者,那么业务层实际上是[域名]模型(Entities and Value Objects,您在实体和对象中以面向对象的方式设计业务逻辑),以及应用层来协调事务和操作以及命令到域层],因此建议的体系结构将类似于这样:

  • 演示文稿(MVC)[OrderView,OrderPresentationModel,OrderController]
  • 申请[OrderService]
    • 使用UnitOfWork(Transactions)和存储库执行域逻辑
    • DTO [OrderDTO,CustomerDTO]
    • 实体和价值对象[订单,客户,LineItem,地址]
    • 存储库接口[IOrderRepository,ICustomerRepository] ​​
    • (可选)工作单元界面[IUnitOfWork]
  • Infrastructure.DataAccess(使用ORM或数据访问技术)
    • 存储库[OrderRepository,CustomerRepository] ​​
    • (可选)工作单元[UnitOfWork]
  • Infrastructure.Common
    • 登录
    • 实用程序

示例场景:

[演示] OrderController:

 _orderService.CreateOrder(OrderDTO);

[申请] OrderService:

 _unitOfWork.BeginTransaction();
 var customer = _customerRepository.GetById(orderDTO.CustomerId);
 var order = new Order() { Customer=customer, Price=orderDTO.Price, ... }
 _orderRepository.Add(order);
 _unitOfWork.Commit();

关于您的问题:

1)将事务控制暴露给MVC是一个好主意吗?交易控制应该在哪个阶段发生?在我看来,MVC不应该对交易负责,但如何避免这种情况呢?

不,我更愿意在应用层中将其分开,以便使设计灵活,以支持不同的演示文稿。

2)是否应该有一些自动处理交易的方法?此ActionFilter实现是半自动的,但事务控制显然位于MVC部分,而不是业务层。

在应用层使用交易。

3)UnitOfWork类与业务层类相同吗? - 如果是这样,这是否意味着我们可以在其中添加自定义业务逻辑方法? - 如果没有,我们是否将工作单元包含在包含业务逻辑方法的其他类中?

不,这只是将任务分组到事务中的一种方法。 业务逻辑实际上封装在实体中,如果逻辑与一个实体无关,则应在域服务[TransferService.Transfer(account1,account2,amount)]中实现,用于协调,访问存储库和应用程序的事务层就是这个地方。

答案 1 :(得分:1)

你有没有看过S#arp Architecture?它使用动作过滤器内置MVC事务处理。

我通常是一个分层纯粹主义者,所以我对让MVC打开视图并不感到兴奋,但我喜欢它。

有专家和骗局,但这里有一些Open Session In View pattern.的优势:

  1. 延迟加载 - 如果您可以依赖MVC视图中的延迟加载,它将简化您的数据访问。无需显式连接到视图所需的所有表。 (当延迟加载会产生大量的查询时,请务必通过明确加入来避免N+1 problem。数据网格是这里常见的罪魁祸首。)
  2. 更简单的BSO层 - 您的BSO无需担心会话。假设您已经在会话的上下文中工作。
  3. 如果你不想使用S#arp,你仍然可以在ActionFilter中只用几行代码自己实现Open-Session-In-View。在Action Executing方法上,打开会话。在动作执行的方法中,提交,关闭和处置。这就是我们在项目中所做的事情,它对我们来说效果很好。

答案 2 :(得分:0)

任何基于Web的应用程序存在的问题是,由于需要通过HTTP会话管理对象生存期,因此必须将UI与业务层进行一些耦合。在典型的桌面应用程序中,您不必担心会话,因此可以轻松地将所有事务处理转移到链中。

考虑在三个应用程序,网站,Web服务和桌面应用程序中重用相同逻辑的位置。在不将事务处理暴露给表示层的情况下,没有好的方法来处理事务提交,因为业务层不知道对象将存在多长时间。

因此,您可以选择确保在商业对象的每个方法中提交所有内容,或者将事务公开给UI。

第三种选择是构建一个相当复杂的会话管理控制器,我甚至不想考虑......会话管理控制器可以耦合到ui和业务逻辑,在某种程度上将它们分开..但这需要更多的分析。