domain-driven-designresponsibility

Interface with service layer or domain objects themselves? (DDD)


I'm still learning about DDD and I have these two (probably simple) questions:

If a Factory creates new object/graph/aggregate instances, but also "reconstitutes" objects/graphs from the Repository, then:

(1) Does your service layer functions/jobs/tasks/unit-of-work call into the Factory or a behavioural method on the Entity instance or a DomainService function? I'm lost as to the call stack based on the responsibility of these components.

(2) Do Entity instances even have "behavioural methods" like above? For example does a Post have p.UpdatePost(string bodyText) or is that not a concern of the domain model and so the same should be achieved with the Repository? Or the service layer function, should it be calling the Repository in this case and the entity instance simply have behavioural methods that are specific to the domain and not persistence? But then, why does it sound like "updating a post" is a domain function when that's the user's goal?

You can see I'm all over the place. Please help.


Solution

  • (1) Does your service layer functions/jobs/tasks/unit-of-work call into the Factory or a behavioral method on the Entity instance or a DomainService function? I'm lost as to the call stack based on the responsibility of these components.

    Usually - top level retrieves necessary aggregate root and calls a function on it. Sometimes top level retrieves multiple aggregate roots and pass them to domain service, but not often because domain service is a quite strong sign that there is unrecognized aggregate root. At the end - top level ensures aggregate root is persisted.

    (2) Do Entity instances even have "behavioural methods" like above? For example does a Post have p.UpdatePost(string bodyText) or is that not a concern of the domain model and so the same should be achieved with the Repository? Or the service layer function, should it be calling the Repository in this case and the entity instance simply have behavioural methods that are specific to the domain and not persistence? But then, why does it sound like "updating a post" is a domain function when that's the user's goal?

    Yes, they do. Domain model should be aware of it's state changes. And that's much more beneficial as it seems at first. Great thing about this is that You gain extensibility point. If client will walk week later to You and say that he wants system to check additional things when user updates post - instead of searching every line of post.bodyText="new value", You will be able to go straight to post.UpdatePost method and attach necessary things there.

    On the other hand - CRUD is not mutually exclusive with domain driven design. E.g. - in my application, management of users and their roles is uninteresting enough that I'm not even trying to model it granularly. You need to recognize parts what matters in business Your application is describing and working with.

    Keep in mind that domain driven design makes sense for complex applications only. Simple blog application doesn't need it.

    (3) Am I wrong in assuming that a service layer (not Domain Services) should encapsulate how an interface interacts with the Domain Layer?

    As I see it - application services are more for orchestrating infrastructure. If there is no infrastructure involved - then application service loses value:

    Application services basically are just facades. And every facade is bad if complexity it adds overweights problems it solves.


    Inside domain:

    //aggregate root is persistence ignorant. 
    //it shouldn't reference repository directly
    public class Customer{
      public string Name {get; private set;}
      public static Customer Register(string name){
        return new Customer(name);
      }
      protected Customer(string name){
        //here it's aware of state changes.
        //aggregate root changes it's own state
        //instead of having state changed from outside
        //through public properties
        this.Name=name;
      }
    }
    
    //domain model contains abstraction of persistence
    public interface ICustomerRepository{
      void Save(Customer customer);
    }
    

    Outside of domain:

    public class CustomerRepository:ICustomerRepository{
      //here we actually save state of customer into database/cloud/xml/whatever
      public void Save(Customer customer){
        //note that we do not change state of customer, we just persist it here
        _voodoo.StoreItSomehow(customer);
      }
    }
    
    //asp.net mvc controller
    public class CustomerController{
      public CustomerController(ICustomerRepository repository){
        if (repository==null)throw new ArgumentNullException();
        _repository=repository;
      }
      public ActionResult Register(string name){
        var customer=Customer.Register(name);
        _repository.Save(customer);
      }
    }