Andreas Zita
Andreas Zita

Reputation: 7560

Right and wrong about services in DDD

Please correct me if I'm wrong (and add other things you think is correct):

Application services ...

Domain services ...

Upvotes: 4

Views: 5596

Answers (2)

Alexey Zimarev
Alexey Zimarev

Reputation: 19600

Your definition of application services is correct. I see application services more as command handlers. Receive a command, load an aggregate, call the aggregate method and save the chance. One command is handled within a single transaction.

Domain services are used to do something that aggregates need but cannot do. Typical examples could be retrieving additional information from the outside world or doing some calculations. The app service doesn't necessarily know if the aggregate would need this or that information but it can resolve necessary dependencies and pass the domain service to the aggregate when it calls it.

In my practice, domain services are most often implemented as functions. Remember that domain services aren't exclusively used by aggregates. Complex value objects can perfectly use domain services for the same purpose.

In my book, I use a domain service to allow a value object to ensure that it will not be instantiated with text that contains profanity.

    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);
    }

Hence that the domain service contract (a named delegate in that case) is defined in the domain,

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

but its implementation is the infrastructure concern and is being wired on the application side.

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);
        }
    }
}

Upvotes: 6

VoiceOfUnreason
VoiceOfUnreason

Reputation: 57194

I believe your understanding of Domain Service is off the mark.

The right starting point is going to be chapter 5 of Domain Driven Design by Eric Evans, where he defines Value Object, Entity and Domain Service.

As best I can tell, Evans was basing his patterns on experiences gathered writing domain models using Java circa 2003. In Java, anything that isn't a domain agnostic primitive is "an object"; while you could implement static functions, there weren't any particularly good ways to pass them. You instead needed to wrap the function inside of an object.

So "Domain Services" are "stateless objects"; objects, because that was a constraint in passing them, and stateless because all of the mutation of the underlying data is the responsibility of the entity that manages that data.

In the text, I believe he uses the example of a tax table; an invoice needs to be able to calculate taxes correctly, but the tax code isn't owned or managed by an invoice instance; instead, that data is managed elsewhere, and a read-only copy is shared by all of the invoices in the model.

In the Cargo shipping example, Cargo need to be assigned to routes, but the Cargo entities don't manage their own copies of the shipping schedules. Instead, queries against those tables are supported by the "RoutingService".

Coordination of entities, what Robert Martin referred to as Use Cases, is an application concern, not something managed by the domain services (as described by Evans).

Upvotes: 2

Related Questions