域驱动设计中IoC自动布线的选项

时间:2010-07-15 17:14:59

标签: c# domain-driven-design inversion-of-control ioc-container autowired

在我最新的ASP.NET MVC 2应用程序中,我一直在尝试将Domain Driven Design (DDD)Single Responsibility Principle (SRP)Inversion of Control (IoC)Test Driven Development (TDD)的概念付诸实践。作为一个架构示例,我一直在关注Jeffery Palermo的“Onion Architecture”,它在ASP.NET MVC 2 in Action中得到了极大的扩展。

Onion Architecture Diagram

虽然,我已经开始成功应用这些原则中的大部分(一些?),但我错过了这个难题的关键部分。我无法确定将服务层自动连接到域实体的最佳机制。

作为示例:需要能够发送电子邮件的每个域实体都应该依赖于IEmailService接口。从我的阅读中,揭示这种依赖性的最佳实践是使用构造函数注入。在我的UI层中,我使用来自ASP.NET MVC ContribStructureMapControllerFactory对存储库接口实现执行类似的注入。

我感到困惑的是,将必要服务的注入自动连接到域实体的最佳机制是什么?域实体是否应该以这种方式注入?如果我不将其注入域实体,我将如何使用IEmailService

额外的Stack Overflow问题,这些问题是很棒的DDD,SRP,IoC,TDD参考:

1 个答案:

答案 0 :(得分:4)

除非我误解你的意图,而是我选择专注于语义,我将剖析这句话“作为一个例子:每个需要发送电子邮件能力的域名实体应该依赖于IEmailService接口。“

我不得不说这本身就是DDD的极端混蛋。为什么域实体需要依赖电子邮件服务? IMO它不应该。没有理由这样做。

但是,有一个业务操作与域实体一起需要发送电子邮件。您应该在此类中包含IEmailService依赖项,而不是域实体。这个类很可能属于几个几乎同义的名称之一:模型,服务或控制器,取决于你所在的架构/层。

此时,您的StructureMapControllerFactory会正确自动连接使用IEmailService的所有内容。

虽然我可能会略微过于概括,但是将域实体设置为POCO或几乎是POCO(以避免违反SRP)是非常标准的做法,但是为了序列化和验证,域实体中经常违反SRP。对于那些类型的跨领域问题,选择违反SRP更多的是个人信仰立场,而不是“正确”或“错误”决定。

作为最后的跟进,如果您的问题是关于真正在独立服务中运行的代码部分,无论是基于Web还是基于操作系统,以及如何连接依赖关系,那么正常的解决方案将接管服务在基础级别,并以与StructureMapControllerFactory在MVC中相同的方式将IOC应用于它。如何实现这一点完全取决于您正在使用的基础设施。

<强>响应:

假设您IOrderConfirmService有方法EmailOrderConfirmation(Order order)。你最终会得到这样的东西:

public class MyOrderConfirmService : IOrderConfirmService
{    
    private readonly IEmailService _mailer;

    public MyOrderConfirmService(IEmailService mailer)
    {        
        _mailer = mailer;        
    }

    public void EmailOrderConfirmation(Order order)
    {        
        var msg = ConvertOrderToMessage(order); //good extension method candidite
        _mailer.Send(msg);        
    }    
}

然后,您将拥有类似

OrderController
public class OrderController : Controller
{    
    private readonly IOrderConfirmService _service;

    public OrderController(IOrderConfirmService service)
    {        
        _service= service;        
    }

    public ActionResult Confirm()
    {      
            _service.EmailOrderConfirmation(some order);

            return View();
    }    
}

当您正确使用构造函数注入时,StrucutreMap将固有地构建您的整个架构链。这是紧耦合和控制反转之间的根本区别。因此,当StructureMapFactory构建您的控制器时,它将首先看到它需要IOrderConfirmService。此时它将检查是否可以直接插入IOrderConfirmService,因为它需要IEmailService。因此,它将检查它是否可以插入IEmailService,并且对于参数,我们可以说它可以。所以在这一点上它将构建EmailService,然后它将构建MyOrderConfirmService并插入EmailService,然后最终构建OrderController并插入MyOrderConfirmService。这就是术语控制反转的来源。 StructureMap将首先在整个依赖关系链中构建EmailService,并以Controller结尾。在紧密耦合的设置中,这将首先构建Controller,并且必须构建业务服务然后构建电子邮件服务。与IOC相比,紧耦合设计非常脆弱。