1

阅读一些有关领域驱动设计的有趣书籍,其中一个示例如下:

// Order
IOrderNumberService orderNumberService;
public void Accept()
{
    this.orderNumber = this.orderNumberService.GetNextAvailableNumber();
}

上面代码中没有显示的是,OrderNumberService已经被 IoC 框架注入。我们不知道服务对象的生命周期范围(可以为每次注入创建一个新服务,或者每次注入一个单例)。

对于其工作是从数据库中检查下一个可用数字的服务,我认为不需要状态并且可以安全地(可能应该)使用单例。

对于这种情况,我认为不需要换掉这个服务,并且可以直接调用单例类的具体实现OrderNumberService.GetNextAvailableNumber(),或者至少如果我需要使用一个接口进行抽象,我可以在以后随时重构。

除了从实现中抽象出服务接口允许更轻松的测试之外,注入服务还有其他好处吗?

我很谨慎地将我读过的所有东西都写进代码中,因为它们很酷——那么抽象在什么时候创建了这么多的层,使得代码更难阅读,应该注意哪些警告信号?

4

2 回答 2

3

服务在 DDD 中没有状态 (Evans p. 107),无论它们属于哪个层

当域中的重要过程或转换不是实体或值对象的自然责任时,将操作添加到模型中作为声明为服务的独立接口。根据模型的语言定义接口,并确保操作名称是 UBIQUITOUS LANGUAGE 的一部分。使服务无状态

使用高级抽象(接口)而不是实现(类)是依赖倒置原则,而不是依赖注入,依赖倒置是关于抽象和实现的关系,而依赖注入是关于删除硬编码的依赖——即使你的领域是一个接口,您仍然需要以某种方式对其进行初始化,即为某个类实例分配一个值。

至于注入服务实例的技术,DDD 与技术无关。如果您认为使用 IoC 容器会使事情变得更难阅读,您可以考虑改用服务定位器模式,完整的推理请参见Martin J Fowler 关于 DI 容器的文章。但是 tbh DDD 需要一些复杂的团队(Evans 在一些关于 infoq 的谈话中提到过)所以我认为将是能够掌握 IoC 概念的人,所以服务定位器在这里没有太大的优势(除非你正在工作是一些有限的环境,如 IoC 框架过于重量级的 Android 手机解决方案)

至于臭名昭著的单例模式(用静态类方法实现)我认为它根本不合适,因为它不可测试。

DDD 还提倡使用轻量级框架(参见架构框架,Evans p. 74),而现代框架(Spring、Fluent NHibernate 等)的开发考虑到了这一点,因此它们实际上旨在使 POJO/POCO 的工作更简单(您可以在 Fluent NHibernate 架构师 Jeremy Miller http://codebetter.com/jeremymiller/2010/01/11/patterns-in-practice-a-retrospective/撰写的一组关于框架设计的文章中阅读更多内容),许多Spring项目,例如直接引用DDD。因此,框架不太可能强迫您在代码中添加依赖项,或者降低其可读性 - 它宁愿使用反射 API 来设置值,并且会使用一些非侵入性的方法,如 XML 或属性(注释)配置。

于 2013-01-26T19:21:36.700 回答
2

除了从实现中抽象出服务接口允许更轻松的测试之外,注入服务还有其他好处吗?

依赖注入通过易于管理的配置将元素连接在一起。正如您所指出的,一个用例是使用模拟服务测试组件。总的来说,未来可能会出现这种服务实现的许多不同变体,通过配置来切换它们会更容易,而不是在很多地方重构代码。

虽然OrderNumberService.GetNextAvailableNumber()最初可能看起来是微不足道的实现,可能不会改变,但从理论上讲,未来的需求仍然可能发生变化。

想象一下这种情况:您的客户使用您的系统获得了一些其他业务,并希望您在那里安装另一个系统实例。但是他们的会计部门在对订单进行编号时使用了一些基于日期的模式,而不是简单地增加 ID。通过使用 DI,您可以让两个实例运行不同的配置,而无需重构代码。

于 2013-01-27T01:53:00.323 回答