业务逻辑和数据访问层的循环依赖

Posted

技术标签:

【中文标题】业务逻辑和数据访问层的循环依赖【英文标题】:Circular dependency of Business logic and Data access layer 【发布时间】:2016-06-28 16:23:48 【问题描述】:

我正在关注分层架构的 MVC 项目。 在网上阅读和研究之后,我发现拥有单独的层是最佳方法。 所以,我的图层是:

表示层有:控制器、视图 业务层:单独的类库项目(包括域模型(表示表实体)、业务逻辑服务、单独的 ViewModel 文件夹) 数据访问层:调用数据库(SQL 语句、连接)

现在问题来了:

对数据访问层的 BLL 调用:

    public PartnerOperation(IDataAccess dataRepository)
    
        _dataAccess = dataRepository;
    

    public void InsertRequest(PartnerRequestModel partnerRequestModel)
    
      _dataAccess.InsertIntoDB(partnerRequestModel); //Domain object passed to DLL method
    

现在,我的 BLL 依赖于依赖于 BLL 的数据访问层,因为域对象位于 BLL 内部。因此,两者都相互引用。

我苦苦寻找了几个星期,但找不到出路。

我已经经历过了 Business Logic Layer and Data Access layer: circular dependency 但它并没有完全解决我的问题。

一些网站支持分层架构,一些声称洋葱方法更好。 例如:this article 声称整个方法(控制器 -> BLL ->DLL)不是最优的。

    如何克服循环依赖? 我构建此 Web 应用程序的方法有效吗?

【问题讨论】:

【参考方案1】:

您可以使用洋葱架构,也称为 Hexagonal 或 Ports & Adapters(同一事物略有不同的变体)。

使用此架构,您的持久性(数据)层引用您的域层,因此您的存储库等可以返回域实体。为了从域层使用存储库,您需要将存储库的接口放置在域层中,并使用 IoC 容器将它们连接到实现(在持久层中)。

编辑

这听起来好像你不是从你的术语和你提供的代码示例中做 DDD,我猜你因为术语存储库而包含了 DDD 标签,所以我将继续使用非 DDD、n 层和分层术语。

您将看到一个几乎相同的调用堆栈。它将是控制器 -> 服务 -> 存储库。理想情况下,您需要在服务中引用“工作单元”,而不是直接引用存储库。

唯一的区别是项目引用,而不是 BLL 引用 DLL,它会反过来。您的控制器仍将调用 BLL 中服务中的代码。只是您的 BLL 服务不会引用 DLL。因此,为了解决这个问题,您将来自 DLL 存储库的接口放入 BLL 中,并使用 IoC 容器(如 Ninject 或 Castle Windsor)将它们连接起来。

您可能需要查看其他一些主题,例如依赖注入 (DI)(通过构造函数传递依赖项)、控制反转 (IoC)(配置的具体接口类型的自动实例化的大型全局映射)和对于更长期的目标,可能是领域驱动设计 (DDD),以了解使用 Onion 架构所获得的一些优势。

【讨论】:

意思是:控制器调用持久层(DLL)->调用业务层(有业务逻辑,领域模型类)。如果是,那么遵循这种方法是否可靠?如果解释有误,请纠正我。 我已经扩展了我的答案。希望它会有所帮助。我刚刚意识到,在洋葱架构有意义之前,您可能还需要熟悉许多其他术语。【参考方案2】:

业务对象与数据对象不同。您的业​​务对象应该包含业务登录,而数据对象是为了持久性而制作的。如果使用简单的分层架构,当需要在层之间发送数据时,可以将业务对象映射到数据对象。您可以通过编写映射代码或使用 Automapper 等工具进行映射。

这里的总体问题是您保留了视图模型,使业务逻辑层变得多余。如果您选择此路径,您可以在 DAL 中定义您的实体并在 BLL 中使用它们,因为它们所拥有的只是数据。

当您开始关心将域模型与持久性模型分开时,这将是另一回事,您可能会来到 DDD,但您计划的不是 DDD。如果你想要一些 MVC 上的基本示例和某种 DDD,this is what I was able to find quickly,我相信还有更多可用的示例。本文给出了 MVC 和 EF 的示例,并以合理的方式解释了 DDD 背后的一些基础知识。我希望这对你来说是一个很好的起点。您可能会对 Pluralsight 上的一些课程感兴趣。

【讨论】:

这个链接真的很有帮助。我是否应该为 DLL 创建单独的数据实体并避免使用真实的域模型,这会帮助我克服循环依赖吗?在链接中,BLL 正在调用 IRepository 并将域对象发送到 DLL。在我的代码中,它导致了循环依赖。你能对此提供一些见解吗?【参考方案3】:

循环依赖可能表示糟糕的设计,尤其是耦合。如果 A 依赖于 B 而 B 依赖于 A,那么您可能缺少第三个实体 C。因此 A 依赖于 B 并且两者都依赖于 C。多层架构不一定意味着三层解决方案。也可以在需要时将您的业务层拆分为两个程序集。

【讨论】:

以上是关于业务逻辑和数据访问层的循环依赖的主要内容,如果未能解决你的问题,请参考以下文章

三层架构各层之间的依赖关系是啥?

EF--封装三层架构IOC

画图带你彻底弄懂三级缓存和循环依赖的问题

如何设计业务逻辑层

用于业务逻辑或数据访问层的 Web 服务

Spring-三级缓存和循环依赖