20

您对以下“通用”代码优先洋葱启发的 ASP.NET MVC 架构有何看法: 解决方案资源管理器的屏幕截图

层,解释:

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

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

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

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

所以引用是这样的: UI -> 业务服务 -> 存储库 -> 核心对象

我喜欢它的地方:

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

百感交集:

  • 好吧,我们有一个可插入的存储库实现,但是你真的多久有一次相同持久性接口的不同实现?
  • UI 也是如此——我们拥有针对相同业务规则实现不同 UI 应用程序的技术能力,但是当我们可以简单地呈现不同的视图(移动设备、桌面设备等)时,我们为什么要这样做呢?
  • 我不确定 UI 是否应该只通过视图模型与业务层通信,或者我应该像现在一样使用域模型来传输数据。对于显示,我使用视图模型,但对于数据传输,我使用域模型。错误的?

我不喜欢的:

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

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

谢谢

4

4 回答 4

26

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

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

我在这里看到的是它并不是真正的洋葱架构。您忘记了最外层,即“依赖解析”层。在 Onion 架构中,由这一层将核心接口连接到基础设施实现(您的持久性项目应该驻留的地方)。

以下是您应该在 Onion 应用程序中找到的内容的简要说明。核心层的内容是业务独有的一切:领域模型、业务工作流……这一层将所有技术实现需求定义为接口(即:存储库接口、日志接口、会话接口……)。核心层不能引用任何外部库,也没有特定于技术的代码。第二层是基础设施层。这一层提供了非业务核心接口的实现。这是您调用数据库、Web 服务的地方……您可以引用提供实现所需的任何外部库,根据需要部署尽可能多的块 :-)。第三层是你的 UI,你知道在里面放什么 ;-) 最后一层,就是我上面提到的依赖解析。

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

下面是它的样子:

洋葱应用结构

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

核心:包含领域模型

是的,这是正确的地方!

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

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

BusinessServices - 存储库周围的业务层。所有的业务逻辑都应该在这里

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

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

凉爽的 :-)

您将需要添加一个“Bootstrapper”项目,只需在此处查看如何继续。

关于你的复杂感受:

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

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

关于你不喜欢的:

在经典的 Onion 架构中,核心仅被下一层引用。

错误的 !:-) 每一层都需要核心才能访问其中定义的所有接口。

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

再一次,只要编辑与您的 EDMX 关联的 T4 模板真的很容易,这不是问题。您只需要更改生成文件的路径,就可以在基础设施层中拥有 EDMX,在 Core.Domain 项目中拥有 POCO。

希望这可以帮助!

于 2013-04-15T15:59:08.480 回答
3

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

您最后一个问题的可能答案。

  1. 领域
  2. DI
  3. 基础设施
  4. 介绍
  5. 服务
于 2013-04-13T13:46:50.837 回答
2

在我看来:

  • “在经典的 Onion 架构中,核心仅被下一层引用。” 这不是真的,核心应该被任何层引用......记住层之间的依赖方向是朝向中心(核心)。

在此处输入图像描述

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

  • 关于你的 EF,它在错误的地方......它应该在基础设施层而不是核心层。并使用 POCO Generator 在 Core/Model 中创建实体(POCO 类)。或使用自动映射器,以便您可以将核心模型(业务对象)映射到实体模型(EF 实体)
于 2014-03-03T23:17:40.533 回答
1

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

百感交集:

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

Pluggable 经常被吹捧为很好的设计,但我从未见过一个团队将某项的主要实现换成其他的。他们只是修改现有的东西。恕我直言,“可插拔性”仅对能够模拟组件以进行自动化单元测试有用。

我不确定 UI 是否应该只通过视图模型与业务层通信,或者我应该像现在一样使用域模型来传输数据。对于显示,我使用视图模型,但对于数据传输,我使用域模型。错误的?

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

我不喜欢的:

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

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

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

我认为这是实体框架的结果。如果你在“代码优先”模式下使用它,你实际上可以——而且通常会——在持久层中拥有上下文和存储库,在你所谓的核心中使用域(表示为 POCO 类)。

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

正如我上面提到的,除了允许自动化单元测试外,我不会担心需要交换东西。除非有您知道的特定要求,否则这很有可能。

祝你好运!

于 2014-07-05T10:37:13.377 回答