I was reading this article recently: http://enterprisecraftsmanship.com/2016/09/08/domain-services-vs-application-services/

The writer recommends a class like this:

public sealed class AtmService // Domain service
    public void WithdrawMoney(Atm atm, decimal amount)
        if (!atm.CanDispenseMoney(amount))
        decimal amountWithCommission = atm.CalculateAmountWithCommission(amount);
        Result result = _paymentGateway.ChargePayment(amountWithCommission);
        if (result.IsFailure)

Why doesn't the WithdrawMoney method go in the ATM class rather than the ATMService class? Surely this is almost as bad as putting all the domain logic in the application service?

Q1) Is it bad practice to use a domain object A (or list of domain object A's into domain object B when domain object A (or list of domain object A) is not an instance variable of domain object B? For example:

public class Customer
   bool valuedCustomer=false;

   public void IsValuedCustomer (List<Order> orders)
      if (orders.Count > 100)
           //Do some more logic here e.g. look how much they have spent etc
           valuedCustomer = true;

Here the Orders List is passed to the Cutomer class so that it can be determined if they are a valued customer.

Q2) Is a domain service just used to figure out a value (could be object, list etc) that is not an instance variable of an entity/value?


Following on from Ewans answer. Say I had a class like this:

public class Customer
       List<Offer> offers = List<Offer>(); //this is the offers the customer is entitled to e.g. two offers out of ten.

       public void AssignOffers (List<Offer> offers)
          //All available offers (say 100) are passed to this method
          //There is logic here to figure out what offers the person is   entitled to (this could be a LINQ query).  If the person is entitled to an offer then add it to the instance variable list

That is an odd example. I think the key thing about it, and the reason is it a service, is the use of _paymentGateway.

In my view Domain services are used when you have an operation which crosses Bounded Contexts.

You have your ATM aggregate root, with Banks and whatever and a PaymentGateway with Payments and such.

You don't want to combine these into one giant agregate/bounded context but you do have an operation that requires objects from both. You need some object in which to bundle up the domain crossing logic, so you invent an 'ATMService' and put it in there.

Its not a domain object in its own right because it doesnt have data, just methods.

Like you say though it is odd naming to have a service and an object both called ATM and its not clear why PaymentGateway would be in a different bounded context to ATM

  • Thanks. +1 for "Domain services are used when you have an operation which crosses Bounded Contexts.". Do you have any links where this is quoted as normal practice? – w0051977 Aug 13 '17 at 10:46
  • Do you ever use Domain Services inside the same bounded context? Is it acceptable to pass one domain object to another domain object (as a method parameter - note that the parameter being passed is not an instance variable of the class containing the method). – w0051977 Aug 13 '17 at 10:49
  • Well my view is that DDD doesn't work in a distributed system, eg webapps, unless you follow an ADM style approach, passing Domain Structs for want of a better term between services. But thats a bit beyond the scope of your question – Ewan Aug 13 '17 at 11:01
  • I am not against ADMs at all - they are reasonable for some systems. My question is whether it is "acceptable" for one domain object to be dependant on another when there is no instance variable. – w0051977 Aug 13 '17 at 11:12
  • I think, classically, no. Because you will have crossed a bounded context. Hence the need for services. But then you do sometimes have data transfer objects in DDD. so you would be deciding on when your dto is 'just a dto' and when its a 'domain object' which is a bit fuzzy. with full ADM at one end and 'No Domain Services at all' at the other – Ewan Aug 13 '17 at 11:16

re: IsValuedCustomer(List orders)

This is a 'bad' method because either:

  • Order should be part of the Customer Aggregate Root. In which case you don't need to pass the orders in.

  • OrderCollection is a DomainObject in which case you can have OrderCollection.IsValuedCustomerId(string customerId)

  • Customer is a DomainService, in which case it shouldn't have customer data and the naming is odd.

Having said that though, If you were taking an ADM approach where Order was simply a struct or data transfer object. Having a ICustomerValuationService.ValueCustomerFromOrders() could well be a good approach.

In this case Order is your domain object and Customer is a Domain Service

However, this wouldn't be 'classic' DDD (for want of a better term) in my opinion. Which I think assumes you are programming a monolith style OOP app where everything is held in memory, and thus you don't need to pass data objects around.

  • Thanks. Could you have a look at my update (I have written another class to try and explain my problem). – w0051977 Aug 13 '17 at 14:03
  • your edit doesnt really change anything. if a customer has orders then they should be part of the same agregate – Ewan Aug 13 '17 at 15:00
  • They are part of the same aggregate in the edit, aren't they? i.e. Offer is an instance variable of customer. In the edited version; I am talking about Customer and Offer rather than Customer and Order. Perhaps I did not make that clear? I am asking if it is "bad" to inject a list of offers into customer - then the Customer class figures out what offers the customer is entitled to (using the AssignOffers method). – w0051977 Aug 13 '17 at 15:32
  • if you have Customer.Offers as part of the customer aggregate then there should be no need to inject it with a method – Ewan Aug 13 '17 at 17:08
  • Thanks. I inject all offers into the AssignOffers method. AssignOffers then figures out what offers the customer is entitled to. Does that make sense? – w0051977 Aug 13 '17 at 17:20

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.