业务逻辑和数据访问层的循环依赖
Circular dependency of Business logic and Data access layer
我正在从事遵循分层架构的 MVC 项目。
在网上阅读和研究之后,我发现拥有单独的层是最佳方法。
所以,我的图层是:
- 表示层有:控制器、视图
- 业务层:单独的 class 库项目(包括域模型(表示 table 实体)、业务逻辑服务、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 但它并没有完全解决我的问题。
一些网站支持分层架构,一些网站声称 Onion Approach 更好。
例如:this article 声称整个方法(Controller -> BLL ->DLL)不是最优的。
- 如何克服循环依赖?
- 我构建此 Web 应用程序的方法有效吗?
业务对象与数据对象不同。您的业务对象应包含业务登录,而数据对象是为持久性而创建的。如果您使用简单的分层架构,当您需要在层与层之间发送数据时,您可以将业务对象映射到数据对象。您可以通过编写映射代码或使用 Automapper 等工具进行映射。
这里的总体问题是您坚持您的视图模型,使业务逻辑层变得多余。如果您选择这条路径,您可以在 DAL 中定义您的实体并在 BLL 中使用它们,因为它们只有数据。
当您开始关心将域模型与持久性模型分开时,这将是另一回事,您可能会来到 DDD,但您计划的不是 DDD。如果您想要一些带有某种 DDD this is what I was able to find quickly 的 MVC 基本示例,我相信有更多示例可用。本文给出了 MVC 和 EF 的示例,并以合理的术语解释了 DDD 背后的一些基础知识。我希望这对您来说是一个很好的起点。还有一些您可能感兴趣的 Pluralsight 课程。
循环依赖可能表示设计不当,尤其是耦合。如果 A 依赖于 B 而 B 依赖于 A,那么您可能缺少第三个实体 C。因此 A 依赖于 B 并且两者都依赖于 C。多层架构并不一定意味着三层解决方案。也可以在需要时随意将业务层拆分为两个程序集。
您可以使用洋葱架构,也称为六角形或端口和适配器(在同一事物上略有不同的变体)。
使用此架构,您的持久性(数据)层引用您的域层,因此您的存储库等可以 return 域实体。为了从域层使用存储库,然后需要将存储库的接口放在域层中,并使用 IoC 容器将它们连接到实现(在持久层中)。
编辑
从你的术语和你提供的代码示例来看,这听起来不像是在做 DDD,我猜你因为术语存储库而包含了 DDD 标签,所以我会继续使用非 DDD、n 层和分层术语。
您将看到几乎相同的调用堆栈。它将是 Controller -> Service -> Repository。理想情况下,您需要在服务中引用 'Unit of Work' 而不是直接引用存储库。
唯一的区别是项目引用,而不是 BLL 引用 DLL,相反。您的控制器仍将调用 BLL 中服务中的代码。只是您的 BLL 服务不会引用 DLL。因此,为了解决这个问题,您将 DLL 存储库中的接口放在 BLL 中,并使用像 Ninject 或 Castle Windsor 这样的 IoC 容器将它们连接起来。
您可能需要研究其他一些主题,例如依赖注入 (DI)(通过构造函数传递依赖项)、控制反转 (IoC)(配置的具体接口类型的自动实例化的大型全局映射) 并且对于长期目标可能是领域驱动设计 (DDD) 来理解使用洋葱架构所获得的一些优势。
我正在从事遵循分层架构的 MVC 项目。 在网上阅读和研究之后,我发现拥有单独的层是最佳方法。 所以,我的图层是:
- 表示层有:控制器、视图
- 业务层:单独的 class 库项目(包括域模型(表示 table 实体)、业务逻辑服务、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 但它并没有完全解决我的问题。
一些网站支持分层架构,一些网站声称 Onion Approach 更好。 例如:this article 声称整个方法(Controller -> BLL ->DLL)不是最优的。
- 如何克服循环依赖?
- 我构建此 Web 应用程序的方法有效吗?
业务对象与数据对象不同。您的业务对象应包含业务登录,而数据对象是为持久性而创建的。如果您使用简单的分层架构,当您需要在层与层之间发送数据时,您可以将业务对象映射到数据对象。您可以通过编写映射代码或使用 Automapper 等工具进行映射。
这里的总体问题是您坚持您的视图模型,使业务逻辑层变得多余。如果您选择这条路径,您可以在 DAL 中定义您的实体并在 BLL 中使用它们,因为它们只有数据。
当您开始关心将域模型与持久性模型分开时,这将是另一回事,您可能会来到 DDD,但您计划的不是 DDD。如果您想要一些带有某种 DDD this is what I was able to find quickly 的 MVC 基本示例,我相信有更多示例可用。本文给出了 MVC 和 EF 的示例,并以合理的术语解释了 DDD 背后的一些基础知识。我希望这对您来说是一个很好的起点。还有一些您可能感兴趣的 Pluralsight 课程。
循环依赖可能表示设计不当,尤其是耦合。如果 A 依赖于 B 而 B 依赖于 A,那么您可能缺少第三个实体 C。因此 A 依赖于 B 并且两者都依赖于 C。多层架构并不一定意味着三层解决方案。也可以在需要时随意将业务层拆分为两个程序集。
您可以使用洋葱架构,也称为六角形或端口和适配器(在同一事物上略有不同的变体)。
使用此架构,您的持久性(数据)层引用您的域层,因此您的存储库等可以 return 域实体。为了从域层使用存储库,然后需要将存储库的接口放在域层中,并使用 IoC 容器将它们连接到实现(在持久层中)。
编辑
从你的术语和你提供的代码示例来看,这听起来不像是在做 DDD,我猜你因为术语存储库而包含了 DDD 标签,所以我会继续使用非 DDD、n 层和分层术语。
您将看到几乎相同的调用堆栈。它将是 Controller -> Service -> Repository。理想情况下,您需要在服务中引用 'Unit of Work' 而不是直接引用存储库。
唯一的区别是项目引用,而不是 BLL 引用 DLL,相反。您的控制器仍将调用 BLL 中服务中的代码。只是您的 BLL 服务不会引用 DLL。因此,为了解决这个问题,您将 DLL 存储库中的接口放在 BLL 中,并使用像 Ninject 或 Castle Windsor 这样的 IoC 容器将它们连接起来。
您可能需要研究其他一些主题,例如依赖注入 (DI)(通过构造函数传递依赖项)、控制反转 (IoC)(配置的具体接口类型的自动实例化的大型全局映射) 并且对于长期目标可能是领域驱动设计 (DDD) 来理解使用洋葱架构所获得的一些优势。