域驱动devise:域服务,应用程序服务
有人可以通过提供一些例子来解释域和应用程序服务之间的区别吗? 而且,如果一个服务是一个域服务,我会把这个服务的实际实现放在这个域的程序集中吗?如果是的话,我还会把这个服务注入到这个域服务中吗? 有些信息会非常有帮助。
服务有三种types: 域服务 , 应用程序服务和基础架构服务
- 域服务 :封装自然适合于域对象的业务逻辑 ,而不是典型的CRUD操作 – 这些操作将属于存储库 。
- 应用程序服务 :由外部消费者用来与您的系统交谈(思考Web服务 )。 如果消费者需要访问CRUD操作,他们就会暴露在这里。
- 基础设施服务 :用于抽象技术问题(如MSMQ,电子邮件提供商等)
保持域服务与你的域对象是明智的 – 他们都专注于域逻辑。 是的,您可以将存储库注入您的服务。
应用程序服务通常会同时使用域服务和存储库来处理外部请求。
希望有所帮助!
(如果你不喜欢阅读,底部有一个总结:-)
我也为应用服务的确切定义而苦苦挣扎。 尽pipeVijay的回答在一个月前对我的思考过程非常有帮助,但我却不同意这个观点。
其他资源
关于应用程序服务的信息很less。 诸如聚合根,存储库和域服务之类的主题被广泛讨论,但是应用服务仅被简要地提及或者被完全省略。
MSDN Magazine文章“域驱动devise简介”将应用程序服务描述为将域模型转换和/或公开给外部客户端(例如WCF服务)的一种方式。 这也是维杰如何描述应用程序服务。 从这个angular度来看,应用程序服务是您的域的一个接口 。
Jeffrey Palermo关于洋葱build筑的文章(第一部分, 第二部分和第三部分 )是一个很好的解读。 他将应用程序服务视为应用程序级概念 ,例如用户会话。 虽然这更接近我对应用服务的理解,但仍然不符合我对这个主题的想法。
我的想法
我已经将应用程序服务视为应用程序提供的依赖关系 。 在这种情况下,应用程序可以是桌面应用程序或WCF服务。
域
时间为例。 你从你的域名开始。 所有实体和任何不依赖于外部资源的域服务都在这里实现。 依赖于外部资源的任何领域概念都是由一个接口定义的。 这是一个可能的解决scheme布局(项目名称以粗体显示):
我的解决scheme - My.Product.Core (My.Product.dll) - 域名服务 IExchangeRateService 产品 ProductFactory IProductRepository
Product
和ProductFactory
类已经在核心程序集中实现。 IProductRepository
是可能由数据库支持的东西。 这个实现不是域的问题,因此由接口定义。
现在,我们将重点介绍IExchangeRateService
。 此服务的业务逻辑由外部Web服务实现。 然而,它的概念仍然是该领域的一部分,并由此界面来表示。
基础设施
外部依赖的实现是应用程序基础结构的一部分:
我的解决scheme + My.Product.Core (My.Product.dll) - My.Product.Infrastructure (My.Product.Infrastructure.dll) - 域名服务 XEExchangeRateService SqlServerProductRepository
XEExchangeRateService
通过与xe.com进行通信XEExchangeRateService
实现IExchangeRateService
域服务。 通过包含基础架构程序集,您的应用程序可以使用该实现来利用您的域模型。
应用
请注意,我还没有提到应用程序服务。 我们现在来看看。 比方说,我们希望提供一个IExchangeRateService
实现,它使用caching来快速查找。 这个装饰类的轮廓可能看起来像这样。
public class CachingExchangeRateService : IExchangeRateService { private IExchangeRateService service; private ICache cache; public CachingExchangeRateService(IExchangeRateService service, ICache cache) { this.service = service; this.cache = cache; } // Implementation that utilizes the provided service and cache. }
注意ICache
参数? 这个概念不是我们域的一部分,所以它不是一个域服务。 这是一个应用程序服务 。 这是应用程序可能提供的基础架构的依赖性。 我们来介绍一个应用程序来演示这个:
我的解决scheme - My.Product.Core (My.Product.dll) - 域名服务 IExchangeRateService 产品 ProductFactory IProductRepository - My.Product.Infrastructure (My.Product.Infrastructure.dll) - 应用程序服务 ICACHE - 域名服务 CachingExchangeRateService XEExchangeRateService SqlServerProductRepository - My.Product.WcfService (My.Product.WcfService.dll) - 应用程序服务 MemcachedCache IMyWcfService.cs + MyWcfService.svc + Web.config
这一切都在这样的应用程序中:
// Set up all the dependencies and register them in the IoC container. var service = new XEExchangeRateService(); var cache = new MemcachedCache(); var cachingService = new CachingExchangeRateService(service, cache); ServiceLocator.For<IExchangeRateService>().Use(cachingService);
概要
完整的应用程序由三个主要层组成:
- 域
- 基础设施
- 应用
域图层包含域实体和独立域服务。 依赖于外部资源的任何域概念 (这包括域服务,也包括存储库)由接口定义。
基础结构层包含来自域层的接口的实现。 这些实现可能会引入必须提供给应用程序的新的非域依赖关系。 这些是应用程序服务,由接口表示。
应用程序层包含应用程序服务的实现。 如果基础设施层提供的实现不足,则应用层还可能包含域接口的其他实现。
虽然这种观点可能与一般的DDD服务定义不一致,但它将域与应用程序分开,并允许您在多个应用程序之间共享域(和基础结构)程序集。
帮助我了解应用程序服务和域服务之间差异的最佳资源是Eric Evans货运示例的Java实现,可在此处find。 如果您拒绝,可以查看RoutingService(域服务)的内部结构和BookingService,CargoInspectionService(这是应用程序服务)的内部结构。
我的“哈哈”时刻是由两件事触发的:
- 在上面的链接中阅读服务的描述,更确切地说是这个句子:
域服务以无处不在的语言和域types来表示,即方法参数和返回值是合适的域类。
- 阅读这篇博文 ,特别是这个部分:
我发现把苹果与橘子分开的一个很大的帮助就是在应用程序工作stream程方面思考。 与应用程序工作stream有关的所有逻辑通常最终都是将应用程序服务分解为应用程序层,而来自域的概念似乎不适合作为模型对象,最终形成一个或多个域服务。
域服务是域的扩展。 只能在域名的背景下看到。 这不是一些用户操作,比如closures帐户等 。 域服务适合没有状态的地方。 否则,它将是一个域对象。 域服务只有在与其他协作者(域对象或其他服务)完成时才有意义。 而有意义的是另一层的责任。
应用服务是初始化和监督域对象和服务之间交互的层。 stream程一般是这样的:从存储库获取域对象(或对象),执行一个操作并将其放回(或不)。 它可以做更多 – 例如它可以检查域对象是否存在,并相应地抛出exception。 因此,它允许用户与应用程序进行交互(这可能是其名称的起源地) – 通过操纵域对象和服务。 应用程序服务通常应该代表所有可能的用例 。 在考虑这个领域之前,你可以做的最好的事情就是创build应用程序服务接口,这会让你更深入地了解你真正想做的事情。 拥有这样的知识使您能够专注于域名。
一般来说,存储库可以注入到域服务中,但这是非常罕见的情况。 尽pipe它是大部分时间都是应用程序层。
从“红皮书”(实施域名驱动devise,Vaughn Vernon)开始,我就是这样理解这些概念的:
域对象 ( 实体和值对象 )封装(子)域所需的行为,使其自然,expression和易于理解。
域服务封装了不适合单个域对象的行为。 例如,向图书借出图书的图书馆(具有相应的Inventory
变化)可以通过域服务来实现。
应用程序服务处理用例stream程,包括域顶部所需的任何其他问题。 它经常通过API暴露这种方法,供外部客户使用。 为了构build我们以前的例子,我们的应用服务可能会公开一个方法LendBookToClient(Guid bookGuid, Guid clientGuid)
:
- 检索
Client
。 - 确认其权限。 ( 请注意,我们如何保持我们的域模型免于安全/用户pipe理的担忧,这样的污染可能会导致很多问题,而我们的应用服务则是在这里满足这个技术要求。
- 检索
Book
。 - 调用域服务(传递
Client
和Book
)来处理将书借给客户端的实际域逻辑 。 例如,我认为确认本书的可用性绝对是域逻辑的一部分。
应用程序服务通常应该有一个非常简单的stream程。 复杂的应用程序服务stream通常表明领域逻辑已经泄漏出领域。
正如你所希望看到的那样, 域模型保持这种方式非常干净 ,并且易于理解和与域专家讨论,因为它只包含自己的实际业务问题。 另一方面, 应用程序stream程 也更容易pipe理,因为它解决了域的问题,变得简洁直接。
域服务:实际上不适合单个实体或需要访问存储库的方法包含在域服务中。 域服务层也可以包含自己的域逻辑,与实体和值对象一样,也是域模型的一部分。
应用程序服务:应用程序服务是位于领域模型之上的一个薄层,用于协调应用程序活动。 它不包含业务逻辑,不包含任何实体的状态; 但是,它可以存储业务工作stream事务的状态。 您使用应用程序服务使用请求 – 应答消息传递模式将API提供到域模型中。
Millett,C(2010)。 专业的ASP.NETdevise模式。 威利出版社 92。