【问题标题】:Composition Root in ASP.NET MVC DDD applicationASP.NET MVC DDD 应用程序中的组合根
【发布时间】:2013-03-12 02:40:31
【问题描述】:

我目前正在阅读 Mark Seemann 的“.NET 中的依赖注入”。我想知道编写 DDD ASP.NET MVC 应用程序的最佳方式是什么。

在简化的场景中,一般的经验法则是拥有作为应用程序核心的域模型,并且不会依赖于数据层或表示。它将暴露 Presentation 将使用的某些接口(因此依赖)和 Data Layer 将实现(因此依赖)。所以一切都很好,很清楚。

但是,现在,当我们编写应用程序时。对于 ASP.NET MVC 应用程序,我们将在 global.asax (http://blog.ploeh.dk/2011/07/28/CompositionRoot) 中执行此操作。我们的组合根需要依赖所有层,因为它需要注册所有适用的类型。

这使得所有的依赖关系看起来很混乱,现在表示层有一个对数据访问层的项目引用(在 VS 术语中)。开发人员很容易犯错,直接使用数据访问层的类型,这将有效地耦合这些层。

有没有一种干净的方法来解决这个难题?将 Composition Root 放在表示层之外几乎会很好,但在 MVC 中这是不可能的。

更新

在问了这个问题后,我找到了一个相关的问题: DAL -> BLL <- GUI + composition root. How to setup DI-bindings? 它有一些有趣的解决方案。公认的解决方案几乎是完美的,但是我希望组合根位于表示层之外,并参考表示层而不是其他方式。

其中一个原因是对我来说它在概念上更清晰 - 构图应该放在最重要的位置。另一个原因是,在我的情况下,表示层中已经有许多 DI 对象(主要是用于查看模型映射器的域对象),我也希望将它们组合在一个位置。

虽然这个帖子给了我一些想法,但我认为我正在尝试做的事情可能是可能的。

【问题讨论】:

  • 我在 ASP.net MVC 中使用 DDD 几乎与您想要实现的目标完全一样。我直接从 UI 项目(在Global.asax 中)使用 Ninject 注入所有实现。所有其他类都接收它们需要使用的接口作为参数(因此它们不负责创建对象)。这个项目很大,但这种方法效果很好。例如,如果我想从 Web 更改为桌面,我只需要重新初始化接口(可能再次使用 Ninject),其他都不需要更改
  • It is easy for a developer to make a mistake and use types form Data Access layer directly, which would effectively couple those layers。无论您如何尝试阻止它,很多愚蠢的事情都很容易发生。不要根据用户可能不会做的事情来为您的应用程序建模。对其进行建模,以便正确地做事。
  • 我同意@jgauffin。您应该始终进行代码审查。即使您确实信任其他开发人员以正确的方式做事。如果你不信任 hem,你可能应该做更多的代码审查。
  • 再说一次,这不是您问题的答案,但是图形中看起来漂亮而干净的设计可以掩盖隐藏的复杂性 - 特别是不必要的抽象。您可能会使用演示代码污染您的域类。见 Ayende 的帖子:ayende.com/blog/4784/…

标签: asp.net-mvc asp.net-mvc-3 dependency-injection domain-driven-design


【解决方案1】:

开发人员很容易犯错误并直接使用数据访问层的类型,这将有效地耦合这些层。

无论您如何阻止它,都很容易做很多愚蠢的事情。不要按照开发人员可能不会做的事情来为您的应用程序建模。对其进行建模,以便正确地做事。

他接受的解决方案几乎是完美的,但是我想要 组合根在表示层之外,并参考 表示层而不是其他方式。

我的容器支持您的要求。在每个项目中创建一个单独的模块来注册其他所有内容:

public class CompositionRoot : IContainerModule
{
    public void Register(IContainerRegistrar registrar)
    {
        registrar.RegisterType<ISomeType, SomeType>();
    }
}

在您的 UI 项目中,您只需加载所有 dll:

registrar.RegisterModules(Lifetime.Scoped, 
                          Environment.CurrentDirectory, 
                          "myproject.*.dll");

就是这样(如果您使用[Component] 属性标记您的实现,您也可以用一行替换大多数手动RegisterType 等)。

https://github.com/jgauffin/griffin.container

【讨论】:

  • 您的回答实际上并没有解决这个问题。 “开发人员很容易犯错误,直接使用数据访问层的类型,这将有效地耦合这些层。有没有一种干净的方法来解决这个难题?”
  • 更新中的问题是The accepted solution is almost perfect, however I would like the composition root to be outside of presentation layer, and reference presentation layer rather than the other way. ,我确实回答了。我回答了您在评论中给出的文字(但我现在也将其复制到答案中)。
  • 好的,我删除了反对票。但我仍然不同意你的回答:P
  • 好的,我明白了,所以在你的情况下,你通过动态注册模块,运行时删除直接依赖。有趣,我需要调查一下。
【解决方案2】:

大多数 IoC 容器都公开了将绑定逻辑分组到一个模块中的概念。

效果是只有包含模块的程序集需要知道具体的实现,组合根只需要访问模块。

示例(伪):

// In data access assembly
namespace MyProject.DataAccessLayer
{
    internal class MyRepository : IMyRepository
    {
        // ...
    }

    public class DataAccessModule : IModule
    {
        void Configure(container)
        {
            container.ForInterface<IMyRepository>()
                     .UseType<MyReposutiry>()
                     .Singleton();
        }
    }
}

// In presentation layer assembly
namespace MyWebApp
{
    void Booptstrap()
    {
        var iocContainer = /* ... */

        iocContainer.AddModule(new RepositoryModule());
    }
}

注意,MyRepository 的具体实现类是internal。只有模块需要看到它。

这种模式的自然扩展是每个程序集仅公开地公开一个 IoC 模块,所有其他具体类都是内部实现细节。

【讨论】:

  • 好的,所以在您的情况下,您仍然会有 Presentation 和 DataLayer 之间的项目引用,但是您将无法滥用 DataLayer 类,因为它们是内部的。有趣的。我在这里看到的唯一缺点是您的组件接口直接绑定到 IoC 容器。也就是说,如果您在 autofac 中编写模块注册,则无法将其与用 Ninject 编写的应用程序或Poor Man DI 一起使用。
  • @SebastianK 是的,没错,一个 IoC 容器将无法加载为另一个 IoC 容器编写的模块。 jgauffins 提出的解决方案也会有同样的问题。如果在多个容器上拥有一个抽象层很重要,您可能希望查看CommonServiceLocator - 尽管这不会抽象容器配置。
【解决方案3】:

实现更高程度封装的一种方法是将域公开为 HTTP 服务,例如 ASP.NET WebAPI。然后,ASP.NET MVC 解决方案将引用此 API。不会有数据访问依赖,只有对服务的引用。为简单起见,可以将 API 的已发布语言提取到 MVC 表示解决方案可以引用的程序集中。这种方法的权衡是添加了移动部件以及创建和管理服务所涉及的步骤。

此外,拥有您描述的配置是完全可以接受的。通过提取服务获得的封装可能不值这个价。开发人员应该有纪律来防止您描述的那种泄漏。

【讨论】:

    【解决方案4】:

    我正在研究以 eulerfx 上述方式构建的解决方案(这是系统要求,而不是我自己的设计选择)。它将解决您的问题,尽管您可能需要考虑将您的域模型类与您的域分开。此外,您将需要另一个用于 Web api 服务的组合根,并且正如他所指出的,从 Web 控制器视图模型(必须在服务和网站之间共享)到域模型的进一步映射层。

    我现在使用 .NET 中的依赖注入一书中描述的方法创建了一些解决方案,我可以说使用其中描述的方法肯定意味着更好的质量、松散耦合的代码。

    祝你好运!

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-01-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多