5

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

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

DDD 应用程序中的依赖项

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

DDD 应用程序中的组合根

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

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

更新

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

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

不过,这个帖子给了我一些想法,我认为我想做的事情可能是可能的。

4

4 回答 4

3

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

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

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

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

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

于 2013-03-12T11:41:32.210 回答
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 模块,所有其他具体类都是内部实现细节。

于 2013-03-12T09:31:38.333 回答
1

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

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

于 2013-03-12T03:33:58.357 回答
0

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

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

祝你好运!

于 2013-03-12T16:16:33.833 回答