关于ASP.NET MVC Onion架构的意见

时间:2013-04-12 21:15:17

标签: entity-framework asp.net-mvc-4 onion-architecture

您对以下“通用”代码 - 第一个洋葱风格的ASP.NET MVC架构有何看法: screenshot of the solution explorer

这些图层解释道:

核心 - 包含域模型。例如这是业务对象及其关系。我正在使用Entity Framework来可视化地设计实体及它们之间的关系。它让我为数据库生成一个脚本。我正在获得自动生成的类似POCO的模型,我可以在下一层(持久性)中自由引用,因为它们很简单(即它们不是特定于数据库的)。

持久性 - 存储库接口和实现。基本上是对域模型的CRUD操作。

BusinessServices - 存储库周围的业务层。所有业务逻辑都应该在这里(例如GetLargestTeam()等)。使用CRUD操作组成返回对象或获取/过滤/存储数据。应包含所有业务规则和验证。

Web(或任何其他UI) - 在这种特殊情况下,它是一个MVC应用程序,但这个项目背后的想法是提供UI,由业务服务提供的驱动。 UI项目使用Business层,无法直接访问Repository。 MVC项目有自己的View模型,这些模型特定于每个View情境。我并不想强迫它提供域模型。

所以引用如下: UI - >商业服务 - >存储库 - >核心对象

我喜欢它:

  • 我可以设计我的对象,而不是手动编码。我正进入(状态 代码生成的模型对象。
  • UI由业务驱动/强制执行 层。可以针对相同的UI应用程序编码 商业模式。

感慨万千:

  • 很好,我们有一个可插拔的存储库实现,但是你有多少次真正拥有相同持久性接口的不同实现?
  • UI同样如此 - 我们有能力针对相同的业务规则实施不同的UI应用程序,但是为什么我们可以这样做,何时可以简单地呈现不同的视图(移动,桌面等)?
  • 我不确定UI是否应该只通过View模型与业务层进行通信,或者我应该像现在一样使用域模型来传输数据。为了显示,我使用的是视图模型,但是对于数据传输,我使用的是Domain模型。错?

我不喜欢的内容:

  • Core项目现在在其他所有项目中都被引用 - 因为我想要/必须访问Domain模型。在传统的Onion架构中,核心仅由下一层引用。
  • DbContext在.Core项目中实现,因为它是由实体框架生成的,位于.edmx所在的位置。我实际上想将.EDMX用于可视化模型设计,但我觉得DbContext属于Persistence层,位于特定于数据库的存储库实现中。

作为最后一个问题 - 什么是一个没有过度设计的好建筑(例如一个完整的洋葱,我们有注射,服务定位器等),但同时提供一些合理的灵活性,在某些地方你真实需要的地方吗?

由于

4 个答案:

答案 0 :(得分:25)

哇,这里有很多话要说! ; - )

首先,我们来谈谈整体架构。

我在这里看到的是,它并不是真正的洋葱架构。您忘记了最外层,即“依赖性解析”层。在Onion架构中,由此层将Core接口连接到Infrastructure实现(Persistence项目应该驻留在哪里)。

以下是您应该在洋葱应用程序中找到的内容的简要说明。核心层中的内容是业务所特有的:域模型,业务工作流......此层将所有技术实现需求定义为接口(即:存储库的接口,日志记录接口,会话接口......)。 Core层不能引用任何外部库,也没有特定于技术的代码。第二层是Infrastructure层。该层提供非业务Core接口的实现。这是您调用数据库,Web服务的地方......您可以引用所需的任何外部库来提供实现,根据需要部署尽可能多的块包:-)。第三层是你的用户界面,你知道要放在那里;-)而最新一层,这是我上面谈到的依赖性解决方案。

层之间的依赖关系方向是中心。

以下是它的外观:

Onion App Structure

现在的问题是:如何适应您已经在洋葱架构中编码的内容。

  

核心:包含域模型

是的,这是正确的地方!

  

持久性 - 存储库接口和实现

好吧,你需要将接口与实现分开。需要将接口移动到Core中,并且需要将实现移动到Infrastructure文件夹中(您可以将此项目称为Persistence)。

  

BusinessServices - 存储库周围的业务层。一切   业务逻辑应该在这里

这需要在Core中移动,但你不应该在这里使用存储库实现,只需操作接口!

  

Web(或任何其他UI) - 在这种特殊情况下,它是一个MVC   应用

很酷: - )

您需要添加一个“Bootstrapper”项目,只需查看here即可了解如何继续。

关于你的复杂情绪:

我不会讨论是否需要存储库,你会在stackoverflow上找到很多答案。

在我的ViewModel项目中,我有一个名为“Builder”的文件夹。由我的构建者来讨论我的业务服务接口以获取数据。构建器将接收Core.Domain对象的列表,并将它们映射到正确的ViewModel。

关于你不喜欢的事情:

  

在传统的Onion架构中,核心仅由下一个引用   层

错! :-)每一层都需要Core才能访问那里定义的所有接口。

  

DbContext是在.Core项目中实现的,因为它正在   由实体框架生成,在.edmx所在的位置   是

再一次,只要编辑与EDMX相关的T4模板非常容易,就不会有问题。您只需要更改生成的文件的路径,就可以在Infrastructure层中使用EDMX,在Core.Domain项目中使用POCO。

希望这有帮助!

答案 1 :(得分:3)

我将服务注入我的控制器。这些服务返回位于Core的DTO。 您拥有的模型看起来不错,我不使用存储库模式,但很多人都这样做。我很难在这种类型的架构中使用EF,这就是我选择使用Nhibernate的原因。

对最终问题的可能答案。

  1. CORE
  2. DOMAIN
  3. DI
  4. INFRASTRUCTURE
  5. 演示文稿
  6. SERVICES

答案 2 :(得分:2)

在我看来:

  • “在经典的洋葱架构中,核心仅由下一层引用。” 事实并非如此,Core应该被任何层引用...请记住,层之间的依赖关系方向是朝向中心(Core)。

enter image description here

“上面的图层可以使用它们下方的任何图层”作者:Jeffrey Palermo http://jeffreypalermo.com/blog/the-onion-architecture-part-3/

  • 关于您的EF,它位于错误的位置......它应该位于Infrastructure层中而不是Core层中。并使用POCO Generator在Core / Model中创建实体(POCO类)。或使用自动映射器,以便您可以将核心模型(业务对象)映射到实体模型(EF实体)

另一方面,我使用Onion Architecture创建了一个简单的应用程序来显示层之间的依赖关系以及它应该如何实现。 http://www.jaider.net/posts/935-onion-architecture-in-action-get-started/

enter image description here

答案 3 :(得分:1)

您所做的一切看起来非常不错,基本上是我看到的两种标准架构之一。

  

感慨万千:

     

很好,我们有一个可插拔的存储库实现,但是你有多少次真正拥有相同持久性接口的不同实现?

Pluggable经常被吹捧为好的设计,但我从来没有见过团队换掉某些东西的主要实现。他们只是修改现有的东西。恕我直言"可插拔性"仅适用于模拟自动单元测试的组件。

  

我不确定UI是否应该只通过View模型与业务层进行通信,或者我是否应该使用域模型来传输数据,就像我现在所做的那样。为了显示,我使用的是视图模型,但是对于数据传输,我使用的是Domain模型。错?

我认为视图模型是一个UI(MVC Web)问题,如果您添加了不同类型的UI,例如它可能不需要视图模型或可能需要不同的东西。所以我认为业务层应返回域实体,并允许它们映射到UI层中的视图模型。

  

我不喜欢:

     

Core项目现在在其他所有项目中都被引用 - 因为我想要/必须访问Domain模型。在传统的Onion架构中,核心仅由下一层引用。

正如其他人所说,这是很正常的。通常一切都最终依赖于域。

  

DbContext在.Core项目中实现,因为它是由实体框架生成的,位于.edmx所在的位置。我实际上想要将.EDMX用于可视化模型设计,但我觉得DbContext属于Persistence层,位于特定于数据库的存储库实现中。

我认为这是实体框架的结果。如果您在" Code First"实际上你可以 - 通常也可以 - 在Persistance层中使用Domain(表示为POCO类)的上下文和存储库,在你所谓的Core中。

  

作为最后一个问题 - 什么是一个没有过度设计的好建筑(例如一个完整的洋葱,我们有注射,服务定位器等),但同时提供一些合理的灵活性,在某些地方你真实需要的地方吗?

正如我上面谈到的那样,除了允许进行自动化单元测试外,我不担心需要更换东西。除非有特定的要求,否则你很有可能这样做。

祝你好运!