【问题标题】:Dependency Resolution in Onion Architecture洋葱架构中的依赖解析
【发布时间】:2013-01-31 05:33:20
【问题描述】:

Onion Architecture 是一种构建应用程序以保持关注点分离和松散耦合的方式(示例项目位于:http://onionarch.codeplex.com/)。依赖注入/解析是该架构的一个关键方面,因为它用于将所有层联系在一起。

上面的链接包含一个关于如何使用 Onion 分层构造 ASP.NET MVC 的示例应用程序。我真的很喜欢它,但是这些示例中的大多数都使用 Ninject(我们都知道它非常慢)。我想知道是否有人可以详细说明如何将不同的 DI 工具(如 SimpleInjector、Unity 或 Autofac)集成到 Onion 项目中。

关键是所有层只有1个依赖(包括MVC项目),即Core层。除Dependency Resolution层外,该层可以引用所有层。

我很难将 MVC 项目设置为启动项目,使用 DI,并且在 MVC 层中不包括对 DI 工具的引用。

【问题讨论】:

    标签: .net asp.net-mvc dependency-injection onion-architecture


    【解决方案1】:

    你的问题是

    "如何集成不同的 DI 工具(如 SimpleInjector、Unity 或 Autofac) 到 Onion 项目中?”

    我使用的是 StructureMap 而不是 Ninject,它的集成方式应该适用于任何其他 DI 框架。

    正如你所说,只有依赖解析层应该引用所有其他层,它是洋葱架构的最外层。好吧,为此,我创建了一个名为 BootStrapper 的项目。这是我引用 StructureMap 程序集的唯一项目。 在这个项目的 App_Start 文件夹中,我有一个名为 StructureMapMvc.cs 的文件,如下所示:

    [assembly: WebActivator.PreApplicationStartMethod(typeof(XXXX.BootStrapper.App_Start.StructuremapMvc), "Start")]
    
    namespace XXXX.BootStrapper.App_Start
    {
        public static class StructuremapMvc
        {
            public static void Start()
            {
                IContainer container = IoC.Initialize();
                System.Web.Mvc.DependencyResolver.SetResolver(new StructureMapDependencyResolver(container));
                GlobalConfiguration.Configuration.DependencyResolver = new StructureMapHttpDependencyResolver(container);
                ControllerBuilder.Current.SetControllerFactory(new StructureMapControllerFactory());
            }
        }
    }
    

    有趣的是:

    [assembly: WebActivator.PreApplicationStartMethod(typeof(XXXX.BootStrapper.App_Start.StructuremapMvc), "Start")]
    

    根据掘金包的描述:

    WebActivator 是一个 NuGet 包,允许其他包执行 Web 应用程序中的一些启动代码。

    很酷,对吧?您必须做的最后一件事是确保 BootStrapper 项目程序集将被推送到您的 Web 应用程序的 /bin 文件夹(易于使用构建后操作或 OutputTo 块包)。这将避免您在 MVC 项目中引用 BootStrapper 项目并打破洋葱架构原则。

    因此,所有这些都到位后,它完全符合Composition Root Pattern,并且当您的应用启动时,模块将组合在一起。

    希望这会有所帮助!

    【讨论】:

    • 我在另一个项目中有 DependencyResolution,但是当我使用 Post-build 事件将 DLL 复制到 UI 项目的 /bin 时,它没有连接(断点不是调试时点击等)。但是,如果我在 UI 项目中添加对 DependencyResolution 项目的引用,它确实会连接起来。知道为什么吗?
    • 您是否复制了 UI 项目正常工作所需的所有 DLL?如果您希望能够进行调试,也可以考虑复制 .pdb 文件。
    • 啊,我明白了,忘了一些。为什么它比项目参考更好?
    • 因为在“纯”洋葱架构中,层之间的依赖方向是朝向中心的。由于 Bootsrapper 项目位于最外层,因此不应被任何其他项目引用。
    • 如果我从 MVC(UI) App_Start 引用 BootStrapper 或者 BootStrapper 将调用 MVC(UI) 可以吗?
    【解决方案2】:

    请注意,我认为 Onion 架构(或者至少是您指出的示例实现,正如 @MystereMan 在 cmets 中正确指出的那样)存在您应该注意的问题点。

    虽然该架构似乎偏爱小型/专注的接口(通常只有一个成员),但这些服务的命名似乎另有说明。例如,在参考架构中,有一个 IShippingService 类。它有一个成员,因此它遵守Interface Segregation Principle(这很好)。然而,“运输服务”这个名称表明它应该包含与运输相关的所有方法。那很容易有几十个。但是,将成员添加到此接口会破坏接口隔离原则 Single Responsibility Principle (SRP) 和 Open Close Principle (OCP)。使用许多几乎没有关系(SRP)的方法,实现将变得又大又丑。实施新的运输要求意味着添加一个成员,这会破坏 OCP。接口有很多成员,而消费者通常只需要调用其中一个成员(低内聚度),这会使单元测试更加困难。

    将其分解为所有成员的接口确实可以解决部分问题(架构可能有这种意图),但这会给您留下大量彼此没有关系的接口,因此很难对它们应用横切关注点(日志记录、监控、审计跟踪、验证、事务、容错等)。

    这是否是一个问题取决于很多因素,但违反SOLID 原则之一总是需要提防。

    因此,作为 Onion 架构的补充,我建议您阅读 this article。它描述了这个可能的缺点的解决方案,它可以应用于 Onion 架构。

    【讨论】:

    • 我认为您描述的问题与架构本身无关,而与示例实现有关。我认为洋葱架构中没有任何内在需要您描述的问题。
    • @MystereMan:好点。我确实只看了示例实现。
    猜你喜欢
    • 1970-01-01
    • 2015-05-19
    • 2020-03-23
    • 2014-10-15
    • 2017-11-23
    • 2011-12-08
    • 1970-01-01
    • 2011-10-09
    • 1970-01-01
    相关资源
    最近更新 更多