商业逻辑层应该访问数据库/数据访问层吗?

4

我有点困惑BLL和DAL之间的关系。BLL是否应该通过依赖注入封装DAL?还是BLL仅对领域对象进行操作,而DAL单独执行保存/更新操作?

例如,在典型的MVC应用程序中,假设有一个取消订单的功能,需要你更新订单并更新库存。下面是我的操作吗?

public ActionResult CancelOrder (Guid orderId) {
    Order order = orderRepository.Get(orderId);
    StockItem stockItem = stockRepository.Get(order.StockItemId);

    _orderService.CancelOrder(order, stockItem);
    orderRepository.Update(order);
    orderRepository.Update(stock);
    Return View();
}

或者应该更像以下这样吗?
public ActionResult CancelOrder (Guid orderId) {
    _orderService.CancelOrder(orderId);
    Return View();
}

(within OrderService)
public void CancelOrder(Guid orderId) {
    Order order = orderRepository.Get(orderId);
    StockItem stockItem = stockRepository.Get(order.StockItemId);

    order.Cancelled = true;
    stockItem.AmountInStock = stockItem.AmountInStock + order.Amount;
    orderRepository.Update(order);
    orderRepository.Update(stock);
}

使用这个选项,所有的事情都将由BLL处理,包括数据访问。为了避免紧密耦合,存储库将被注入。任何实体检索都将采用 _orderService.GetOrder(orderId); 的形式,而不是直接访问存储库。
请原谅示例的简陋,因为我没有太多时间。我的所写的内容是否有任何意义,还是我在荒野中迷失了呢?
4个回答

5

绝对不是第一种选项,将业务逻辑嵌入控制器中。问题不在于控制器本身访问数据对象,而在于必须遵循由业务规则指定的流程。这个流程不应该存在于控制器中。

因此,您应该选择第二个选项,或者可能将Cancel作为Order的一个方法。如果您已经编写了类似的代码,请保持一致性。


第二个选项是我更喜欢的。关于在订单类中放置一个取消方法,这会对数据库执行操作吗?通过我的职业生涯,一直向我灌输的理念是领域对象不应该关心数据访问。或者你是指别的什么吗? - David
@David:不,这就是我的意思。有些设计中,领域对象了解它们所属的数据存储。如果这不是你的情况,请忽略 :) - Jon
1
我倾向于将涉及多个实体的逻辑放在服务中,以使持久性(存储库)不涉及实体。 - jgauffin

1

考虑到关注点分离,控制器来自MVC模式,它是一种表示层模式,因此控制器应该包含表示逻辑以支持表示层,而不是业务逻辑。

业务逻辑应该在领域实体中,但也有一些应用逻辑充当存储库之间的协调者,这就是为什么服务层在路上被淘汰的原因。

因此,选项2应该是你的选择。


1

你这里实际上在问两个问题:

控制器和业务层应该包含哪些内容?

=> 我倾向于认为第一个代码片段中的代码是应用程序层服务(如果您承认这两者可以相似,那么控制器也是如此,这是目前有很多讨论的话题)的正确责任级别(因此也适用于控制器)。从存储库获取Order并在取消操作后保存它似乎不是纯业务逻辑。它更多地涉及到周围事务/工作单元和用例的管道。

我只会改变一件事情-尝试一次性保存所有受事务影响的实体的更改。如果您必须在操作结束时手动更新可能发生更改的每个实体,那么这将是一个巨大的痛苦,并且会使控制器无谓地污染。创建一个工作单元系统(或使用现有实现),它将一次性持久化所有更改并删除存储库中的所有Update()方法。

除此之外,正如Jon所建议的那样,我也认为包含Cancel()方法的丰富的Order领域对象比服务更可取-但这是另一个辩论。

BLL和DAL之间应该有什么样的关系?

=> BLL 不应该与 DAL 紧密耦合,作为中心层,它不应直接引用外部层。这样,您可以轻松地在另一个应用程序中重用 BLL,使用另一个 DAL 等。

然而,有时一些业务对象需要直接访问其他对象,它们没有对其进行引用,这基本上意味着从数据库中获取它们。换句话说,BLL 中的某些操作需要与存储库通信。因此,我总是将存储库接口放在 BLL 中,但它们的实现位于 DAL 中,并在运行时注入到 BLL 中。

因此,BLL 与 DAL 仅松散耦合。它保持“持久性无知”,因为它仅操作看起来像中立对象集合的外观(存储库),并且对数据的存储、填充等一切都毫不知情。


我认为我明显更喜欢轻耦合的BLL和DAL。我认为为了让自己保持诚实,我要问的问题是“将这个移植到桌面应用程序有多容易?”,而选项1中的业务规则开始渗入有效前端。我希望我的MVC应用程序只是服务层的薄客户端消费者。同意事务性评论,我经常使用NHibernate将所有内容包装在推荐的事务中。 - David
“业务规则开始悄悄地渗透到实际的前端。”我同意,但这些是应用程序特定的业务规则,而不是领域特定的业务规则。取消订单会导致相应数量的库存增加的规则是领域特定的,无论应用程序如何都不会改变。相比之下,在桌面或移动应用程序中,什么告诉您在取消订单后需要立即保存订单(应用程序特定)?如果用例略有不同,什么告诉您执行的操作的顺序和时间不会改变? - guillaume31
因此,您可能希望将控制器作为仅涉及UI的薄层,并且不涉及应用程序业务规则。但是,在我看来,更好的解决方案是创建一个额外的应用程序服务层,而不是直接将这些特定于应用程序的业务规则放在域(或BLL)中。 - guillaume31

0

BLL应该对您为应用程序创建的业务对象进行操作。理想情况下,它不应该知道数据库和相关操作。如果您希望保持松散耦合的状态,请使用依赖注入来调用DAL中的方法。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接