0

This is not a problem I am having in my problem domain. It is just a thought exercise.

Say I have a simple calculator like this:

public class Calculator
{
     public IEnumerable<KeyValuePair<int, int>> CalculateDenominationsFor(int cost) 
            {
                var target = cost;
                foreach (var denomination in currency.AvailableDenominations.OrderByDescending(a => a))
                {
                   var numberRequired = target / denomination;
                   if (numberRequired > 0)    
             {
                   yield return new KeyValuePair<int, int>(denomination, numberRequired);
               }
               target = target - (numberRequired * denomination); 
            }
    } 
}

As it stands there is no Entity and no Aggregate root.

I believe I have two options:

  1. No aggregate root: Have the application service call the domain service directly i.e. supply a cost and receive the denominations.

  2. Introduce an Aggregate Root: Create a class called ChangeRequest like the following:

    public class ChangeRequest
    {
        public decimal Cost {get; set;}
        public listKeyValuePair<int, int> denominations {get; set;}
    
       public IEnumerable<KeyValuePair<int, int>> AddDenominations(Calculator calculator)
       {
         //Add denominations to list here.
       }
    }
    

Is it normal to have an aggregate without an aggregate root?

  • What is according to you the aggregate (with or without root) ? – Christophe Jan 30 '18 at 21:09
  • @Christophe, a domain model that only contains the Calculator class in the post. – w0051977 Jan 30 '18 at 21:11
  • Ask yourself what the benefits are of introducing this "aggregate root." (fwiw I don't think you have an aggregate or an aggregate root here) – Robert Harvey Jan 30 '18 at 22:20
  • @Robert Harvey, I guess that is because denominations is a KeyValuePair rather than a domain object? – w0051977 Jan 30 '18 at 22:45
  • No, it's because it's not an aggregate. Aggregates and aggregate roots fulfill a specific business function; all you have here is a class and a utility class. Try reading martinfowler.com/bliki/DDD_Aggregate.html – Robert Harvey Jan 30 '18 at 22:51
4

Lets start with a quote form Evans in his DDD reference book:

Sometimes services masquerade as model objects, appearing as objects with no meaning beyond doing some operation.

Keeping this in mind, we can now look at the DDD definitions:

AGGREGATE: A cluster of associated objects that are treated as a unit for the purpose of data changes.

SERVICE: An operation offered as an interface that stands alone in the model, with no encapsulated state.

Calculator has no data an no state. So there is no purpose of data change. Consequently, there is no aggregate. T

The operation CalculateDenominationsFor() is offered as part of Calculator's interface. The class stands alone in the model and has no encapsulated state. Its sole purpose is the execution of the operation. Consequently it's a service.

Adding a fake aggregate root, just because the service is exposed via an object seems like over-engineering.

However there's still room for improvement: the method is not dependent of any object instance, so you could declare it as static. If all the members of that class are static, you could declare the class itself as static. This has the advantage of highlighting the true nature of that class.

  • Thanks. Can I confirm that you are favouring option 1? +1 for: "no purpose of data change". – w0051977 Jan 30 '18 at 22:40
  • @w0051977 I confirm for option 1 – Christophe Jan 30 '18 at 22:52
  • Thanks. Can I ask what you think of the KeyValuePair? Should this be an object rather than a key value pair? – w0051977 Jan 30 '18 at 22:54
  • @w0051977 from the point of view of DDD, a KeyValuePair is already a value object. This being said, I'd encapsulate this in an object in order to decouple the design from the actual implementation and to keep the freedom to let it evolve with more complex data if you want to. – Christophe Jan 30 '18 at 23:09
  • So the keyvaluepair would be represented as an object with two members? Can you give an example of complex data? That is my last question. – w0051977 Jan 30 '18 at 23:23
0

Let me rephrase your question to "whether it's common or not having Application Service consuming Domain Service directly?" Yes, it's common.

One live example is authenticating user in Vaughn Vernon's IDDD. Source code is here.

The author deeply compared two implementations in the chapter of domain service of IDDD book. The first attempt is using the entities directly in Application Service without introducing Domain Service. The main problem with this is that it makes Application Service (the client of domain objects) unnecessary complicated and need it unnecessary to know the internal of the domain objects.

Long story short, using the domain service directly in application service is quite useful when serving the inquiry scenario.

  • What do you mean by inquiry scenario? – w0051977 Jan 30 '18 at 22:59
  • @w0051977 anything about asking questions to the system. e.g. your calculator or authenticate a user etc. – ivenxu Jan 31 '18 at 0:30

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.