ASP.NET MVC DDD应用程序中的组合根

时间:2013-03-12 02:40:32

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

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

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

Dependencies in DDD app

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

Composition Root in DDD app

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

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

更新

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

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

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

4 个答案:

答案 0 :(得分: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

答案 1 :(得分: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模块,所有其他具体类都是内部实现细节。

答案 2 :(得分:1)

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

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

答案 3 :(得分:0)

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

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

祝你好运!