有人能举例说明域服务和应用程序服务之间的区别吗?而且,如果一个服务是一个域服务,我是否会将该服务的实际实现放在域程序集中,如果是的话,我是否也会将存储库注入到该域服务中?一些信息会很有帮助。


当前回答

(如果你不想读,下面有一个总结:-)

我也曾为应用程序服务的精确定义而苦恼。虽然维贾伊的回答对我一个月前的思考过程很有帮助,但我对其中的一部分不同意。

其他资源

关于应用程序服务的信息很少。像聚合根、存储库和域服务这样的主题被广泛讨论,但应用程序服务只被简要提及或完全忽略。

MSDN杂志的一篇文章《领域驱动设计介绍》将应用程序服务描述为一种转换和/或将您的领域模型公开给外部客户端的方式,例如作为WCF服务。Vijay也是这样描述应用程序服务的。从这个角度来看,应用程序服务是域的接口。

Jeffrey Palermo关于洋葱架构的文章(第一部分、第二部分和第三部分)值得一读。他将应用程序服务视为应用程序级别的概念,例如用户会话。尽管这更接近于我对应用程序服务的理解,但它仍然不符合我对这个主题的想法。

我的思想

我认为应用程序服务是应用程序提供的依赖项。在这种情况下,应用程序可以是桌面应用程序或WCF服务。

下面是一个例子。你从你的域名开始。不依赖于外部资源的所有实体和任何域服务都在这里实现。任何依赖于外部资源的域概念都是由接口定义的。下面是一个可能的解决方案布局(项目名称以粗体显示):

My Solution
- My.Product.Core (My.Product.dll)
  - DomainServices
      IExchangeRateService
    Product
    ProductFactory
    IProductRepository

Product和ProductFactory类已经在核心程序集中实现。IProductRepository可能是由数据库支持的。它的实现与域无关,因此由接口定义。

现在,我们将重点关注IExchangeRateService。此服务的业务逻辑由外部web服务实现。但是,它的概念仍然是域的一部分,并由该接口表示。

基础设施

外部依赖的实现是应用程序基础设施的一部分:

My Solution
+ My.Product.Core (My.Product.dll)
- My.Product.Infrastructure (My.Product.Infrastructure.dll)
  - DomainServices
      XEExchangeRateService
    SqlServerProductRepository

XEExchangeRateService通过与xe.com通信实现IExchangeRateService域服务。通过包含基础结构程序集,可以由利用域模型的应用程序使用此实现。

应用程序

注意,我还没有提到应用程序服务。我们现在来看一下。假设我们想要提供一个IExchangeRateService实现,它使用缓存进行快速查找。这个装饰器类的轮廓如下所示。

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参数了吗?这个概念不属于我们的域,因此它不是域服务。它是一种应用服务。它是应用程序提供的基础设施的依赖项。让我们介绍一个演示这一点的应用程序:

My Solution
- My.Product.Core (My.Product.dll)
  - DomainServices
      IExchangeRateService
    Product
    ProductFactory
    IProductRepository
- My.Product.Infrastructure (My.Product.Infrastructure.dll)
  - ApplicationServices
      ICache
  - DomainServices
      CachingExchangeRateService
      XEExchangeRateService
    SqlServerProductRepository
- My.Product.WcfService (My.Product.WcfService.dll)
  - ApplicationServices
      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定义不匹配,但它确实将域与应用程序分离,并允许您在几个应用程序之间共享域(和基础结构)程序集。

其他回答

Domain Services : Methods that don’t really fit on a single entity or require access to the repository are contained within domain services. The domain service layer can also contain domain logic of its own and is as much part of the domain model as entities and value objects. Application Services : The Application service is a thin layer that sits above the domain model and coordinates the application activity. It does not contain business logic and does not hold the state of any entities; however, it can store the state of a business workflow transaction. You use an Application service to provide an API into the domain model using the Request-Reply messaging pattern.

米勒特,C(2010)。专业的ASP。NET设计模式。威利出版。92。

将域服务视为在域对象上实现业务逻辑或业务规则相关逻辑的对象,这种逻辑很难适合相同的域对象,也不会导致域服务的状态更改(域服务是一个没有“状态”的对象,或者更好的是没有具有业务含义的状态),但最终只会更改所操作的域对象的状态。

应用程序服务实现了应用程序级逻辑,如用户交互、输入验证、与业务无关的逻辑,而是与其他关注点相关的逻辑:身份验证、安全性、电子邮件等等。,将自身限制为简单地使用域对象公开的服务。

这方面的一个例子可能是以下场景,只是为了解释目的:我们必须实现一个非常小的domotic实用程序,执行一个简单的操作,即“当有人打开房间的门进入房间时打开灯,当有人关闭房间的门离开房间时关闭灯”。

简化很多,我们只考虑2个域实体,它们不是同一个集合的一部分:门和灯,它们每个都有2个状态,分别是打开/关闭和打开/关闭,以及在它们上操作状态更改的特定方法。实体需要是不同聚合的一部分,这样下面的逻辑就不能在聚合根中实现。

在这种情况下,我们需要一个域服务来执行当有人从外部打开门进入房间时打开灯的特定操作,因为门和灯对象不能以我们认为适合其业务性质的方式实现此逻辑。这个新的域服务需要封装一些应该总是发生的业务流程,由一些域事件/方法触发。

我们可以调用我们的域服务DomoticDomainService并实现两个方法:OpenTheDoorAndTurnOnTheLight和CloseTheDoorAndTurnOffTheLight,这两个方法分别改变对象门和灯的状态为打开/打开和关闭/关闭。

The state of enter or exit from a room it isn't present in the domain service object and either in the domain objects, but will be implemented as simple user interaction by an application service, that we may call HouseService, that implements some event handlers as onOpenRoom1DoorToEnter and onCloseRoom1DoorToExit, and so on for each room (this is only an example for explaining purpose..), that will respectively concern about call domain service methods to execute the attended behaviour (we haven't considered the entity Room because it is only an example).

这个例子,远远不是一个设计良好的现实世界的应用程序,有唯一的目的(已经说过很多次了)解释什么是域服务以及它与应用程序服务的区别,希望它是清晰和有用的。

同样,上面的示例域服务可以很容易地被用于显式实现跨一个或多个聚合的副作用的域事件所取代,但由于这些不是这个问题的主题,所以我在这里只提到它们,以便读者可以意识到它们的存在,然后决定哪种方法更适合它们。

(如果你不想读,下面有一个总结:-)

我也曾为应用程序服务的精确定义而苦恼。虽然维贾伊的回答对我一个月前的思考过程很有帮助,但我对其中的一部分不同意。

其他资源

关于应用程序服务的信息很少。像聚合根、存储库和域服务这样的主题被广泛讨论,但应用程序服务只被简要提及或完全忽略。

MSDN杂志的一篇文章《领域驱动设计介绍》将应用程序服务描述为一种转换和/或将您的领域模型公开给外部客户端的方式,例如作为WCF服务。Vijay也是这样描述应用程序服务的。从这个角度来看,应用程序服务是域的接口。

Jeffrey Palermo关于洋葱架构的文章(第一部分、第二部分和第三部分)值得一读。他将应用程序服务视为应用程序级别的概念,例如用户会话。尽管这更接近于我对应用程序服务的理解,但它仍然不符合我对这个主题的想法。

我的思想

我认为应用程序服务是应用程序提供的依赖项。在这种情况下,应用程序可以是桌面应用程序或WCF服务。

下面是一个例子。你从你的域名开始。不依赖于外部资源的所有实体和任何域服务都在这里实现。任何依赖于外部资源的域概念都是由接口定义的。下面是一个可能的解决方案布局(项目名称以粗体显示):

My Solution
- My.Product.Core (My.Product.dll)
  - DomainServices
      IExchangeRateService
    Product
    ProductFactory
    IProductRepository

Product和ProductFactory类已经在核心程序集中实现。IProductRepository可能是由数据库支持的。它的实现与域无关,因此由接口定义。

现在,我们将重点关注IExchangeRateService。此服务的业务逻辑由外部web服务实现。但是,它的概念仍然是域的一部分,并由该接口表示。

基础设施

外部依赖的实现是应用程序基础设施的一部分:

My Solution
+ My.Product.Core (My.Product.dll)
- My.Product.Infrastructure (My.Product.Infrastructure.dll)
  - DomainServices
      XEExchangeRateService
    SqlServerProductRepository

XEExchangeRateService通过与xe.com通信实现IExchangeRateService域服务。通过包含基础结构程序集,可以由利用域模型的应用程序使用此实现。

应用程序

注意,我还没有提到应用程序服务。我们现在来看一下。假设我们想要提供一个IExchangeRateService实现,它使用缓存进行快速查找。这个装饰器类的轮廓如下所示。

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参数了吗?这个概念不属于我们的域,因此它不是域服务。它是一种应用服务。它是应用程序提供的基础设施的依赖项。让我们介绍一个演示这一点的应用程序:

My Solution
- My.Product.Core (My.Product.dll)
  - DomainServices
      IExchangeRateService
    Product
    ProductFactory
    IProductRepository
- My.Product.Infrastructure (My.Product.Infrastructure.dll)
  - ApplicationServices
      ICache
  - DomainServices
      CachingExchangeRateService
      XEExchangeRateService
    SqlServerProductRepository
- My.Product.WcfService (My.Product.WcfService.dll)
  - ApplicationServices
      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的cargo示例的java实现,可以在这里找到。如果你下载了它,你可以检查RoutingService(域服务)和BookingService, CargoInspectionService(应用服务)的内部结构。

我顿悟的时刻是由两件事引发的:

阅读上述链接中对服务的描述,更准确地说是这句话:

域服务用通用语言和表示 域类型,即方法参数和返回值为 正确的域类。

阅读这篇博文,尤其是这一部分:

我发现区分苹果和橘子有很大帮助的是 从应用程序工作流的角度考虑。所有关于 应用程序工作流通常以应用程序服务结束 考虑到应用层,而来自领域的概念 这似乎不适合模型对象最终形成一个或多个 域服务。

从红皮书(实现领域驱动设计,由Vaughn Vernon)中,我是这样理解这些概念的:

域对象(实体和值对象)封装了(子)域所需的行为,使之自然、有表现力和可理解。

域服务封装了这些不适合单个域对象的行为。例如,一个图书馆可以通过域服务将图书借给客户端(带有相应的库存更改)。

应用程序服务处理用例流,包括域之上所需的任何附加关注点。它经常通过API公开这样的方法,供外部客户端使用。基于前面的例子,我们的应用程序服务可能会公开一个方法LendBookToClient(Guid bookGuid, Guid clientGuid):

Retrieves the Client. Confirms its permissions. (Note how we have kept our domain model free of security / user management concerns. Such pollution could lead to many problems. Instead, we fulfill this technical requirement here, in our application service.) Retrieves the Book. Calls the domain service (passing the Client and Book) to handle the actual domain logic of lending the book to the client. For instance, I imagine that confirming the book's availability is definitely part of the domain logic.

应用程序服务通常应该具有非常简单的流程。复杂的应用程序服务流通常表明域逻辑已经泄漏出域。

正如您所希望看到的,领域模型以这种方式保持非常干净,并且易于理解并与领域专家讨论,因为它只包含它自己的实际业务关注点。另一方面,应用程序流也更容易管理,因为它消除了领域问题,变得简洁明了。