关于DDD服务的对与错

时间:2019-07-10 10:16:12

标签: domain-driven-design

如果我错了,请纠正我(并添加您认为正确的其他内容):

应用程序服务...

  • 域的(公共前端)api
  • 负责加载和保存聚合
  • 可以访问存储库和其他基础架构服务
  • 不是域通用语言的一部分
  • 应该/可能是域顶部的一个很薄的层(主要处理聚合的负载/保存并将其余的委托给域)
  • 可以包含纯读操作

域名服务...

  • 域的“真实” api
  • 包含域逻辑
  • 仅与域对象一起使用(不适用于诸如回购协议和电子邮件发件人服务之类的基础结构服务)
  • 通常包含编排不同聚合的代码
  • 是域通用语言的一部分
  • 可以依赖其他域服务
  • 仅包含修改操作

2 个答案:

答案 0 :(得分:1)

我相信您对Domain Service的理解是不可能的。

正确的起点是埃里克·埃文斯(Eric Evans)的域驱动设计的第5章,他在其中定义了Value ObjectEntityDomain Service。 / p>

据我所知,Evans将其模式建立在使用Java大约在2003年编写域模型的经验上。在Java中,任何与域无关的原语都是“对象”;虽然您可以实现静态函数,但没有 pass 的特别好方法。相反,您需要将函数包装在对象内部。

因此,“域服务”是“无状态对象”;对象,因为这是传递它们的一个约束,而由于所有底层数据的变异都是管理该数据的实体的责任,因此是无状态的。

在本文中,我相信他使用的是税表示例;发票必须能够正确计算税款,但税码不是由发票实例拥有或管理;相反,该数据在其他地方进行管理,并且该模型中的所有发票都共享一个只读副本。

在“货运”示例中,需要将“货运”分配给路线,但是“货运”实体不管理自己的货运时间表副本。相反,“ RoutingService”支持针对这些表的查询。

罗伯特·马丁称为Use Cases的实体协调是应用程序方面的问题,而不是由域服务管理的事情(如Evans所述)。

答案 1 :(得分:0)

您对应用程序服务的定义是正确的。我将应用程序服务更多地视为命令处理程序。接收命令,加载聚合,调用聚合方法并保存机会。一个命令在单个事务中处理。

域服务用于执行聚合需要但不能执行的操作。典型示例可能是从外界检索其他信息或进行一些计算。应用服务不一定知道聚合是否需要此信息,但它可以解决必要的依赖关系,并在调用时将域服务传递给聚合。

在我的实践中,域服务通常是作为功能实现的。请记住,域服务并非仅由聚合使用。复杂值对象可以完美地将域服务用于同一目的。

在我的书中,我使用域服务来允许value object以确保不会使用包含亵渎性文字的实例化。

    public static DisplayName FromString(
        string displayName,
        CheckTextForProfanity hasProfanity)
    {
        if (displayName.IsEmpty())
            throw new ArgumentNullException(nameof(FullName));

        if (hasProfanity(displayName).GetAwaiter().GetResult())
            throw new DomainExceptions.ProfanityFound(displayName);

        return new DisplayName(displayName);
    }

因此,在域中定义了域服务合同(在这种情况下为命名委托),

namespace Marketplace.Domain.Shared
{
    public delegate Task<bool> CheckTextForProfanity(string text);
}

但是它的实现是基础架构方面的问题,并且正在应用程序端进行布线。

namespace Marketplace.Infrastructure
{
    /// <summary>
    /// PurgoMalum is a simple, free, RESTful web service for filtering and removing content of profanity, obscenity and other unwanted text.
    /// Check http://www.purgomalum.com
    /// </summary>
    public class PurgomalumClient
    {
        private readonly HttpClient _httpClient;

        public PurgomalumClient() : this(new HttpClient()) { }

        public PurgomalumClient(HttpClient httpClient) => _httpClient = httpClient;

        public async Task<bool> CheckForProfanity(string text)
        {
            var result = await _httpClient.GetAsync(
                QueryHelpers.AddQueryString("https://www.purgomalum.com/service/containsprofanity", "text", text));

            var value = await result.Content.ReadAsStringAsync();
            return bool.Parse(value);
        }
    }
}