Search code examples
javadesign-patternsdomain-driven-design

Best practices with a domain object collection in DDD


I have the following class:

public class DomainClass {
    private Integer value;
    private Integer total;
    private Integer month;
    public Double getPercent(){/*Business logic*/}
}

I want to do the same getPercent operation with a list of DomainClass objects without repeat code. I got 2 ideas to handle that but I don't know if they're good ones.

Idea 1 - Create a service and iterate the list:

public class DomainObjectService{
....
public Double getPercent(List<DomainClass> list){
    double value, total;
    for(DomainClass d : list){
        value += d.getValue();
        total += d.getTotal();
    }
    // do percent operation
}

Idea 2 - Query operation at database, fill object and call the business method

public class DomainObjectService{
....
public Double getPercent(){
    double value, total;
    .... query data with sql and set the above variables

    // do percent operation
    return new DomainBusiness(value, total).getPercentage();
}

I'm reading that in DDD a entity should handle itself logic but in this case of a collection operation how it should be treated?

Well, if my DDD base knowledge is wrong. I would like to know good article/books/example of DDD in java.


Solution

  • How do you manage your entities? Do you use any kind of ORM?


    My solution for this kind of operations is to build a class that manages the collection of object. So, for example:

    public class DomainClasses{
       private final List<DomainClass> domainClasses;
    
       ....
    
       public Double getPercent(){
          // manage the percent operation   ...
          // ... on all the members the way ... 
          // ... your business is expected  ...
          // ... to do it on the collection
    
          return something;
       }
    
       // class initialization
    }
    

    In this way you can reuse the getPercent code of each class, but also implement a specific version of it to be used by the collection. Moreover, the collection could access to the package private getters, if any, of DomainClass to make this calculations. In this way you expose nothing else than just the functions that you need to build your domain objects.

    Note: this solution is viable if you manage your persistence without any ORM. Or, if you want to use it, it will require additional work to configure correctly the container class.

    Some links:

    https://www.mehdi-khalili.com/orm-anti-patterns-part-4-persistence-domain-model (I work with a DM separated from the PM)

    https://softwareengineering.stackexchange.com/questions/371344/why-not-use-an-orm-with-ddd (is what I'm doing, translating the Domain objects to DTOs that will be persisted - it's a bit of extra code to write for collections, but once is tested it works, always, and what you get is a domain that has less no interferences from ORM framework)


    Update after question. I use the Memento pattern.

    Storing

    My Domain Class has a function that export all the data in a Memento object. The repository takes a Domain Instance, asks for the Memento and then:

    • I generate the SQL insert/update (plain SQL with transaction management from Spring)
    • You can load your JPA entity and update with the Memento information (care should be taken, but if you write tests, once done it will work always - hence, tests are important ;) )

    Reading

    For the reverse, building a Domain instance from the saved data, I do this:

    • in the persistence layer, where the repository code is implemented, I've extended my Memento (lets call it PersistedMemento)
    • when I've to load something, I build a PersistedMemento and I use it to build an instance of the Domain Class
    • my Domain Class has a function that allows to build objects from a Memento. Note: this could not always be necessary, but in my case the main constructor ha extra checks that cannot be done when the object is rebuilt from a saved one. Anyway, this simplifies the rebuilding of the Domain class.

    To protect the Domain classes from being used outside the world of the domain:

    • my repositories require an existent transaction, so they cannot be directly used anywhere in the code
    • the Memento classes have protected constructors, so they are usable only in the Domain package or the Repository package. The PersistedMemento is also hidden in the Repository package, so no instances could be created.

    Notes

    Of course is not the perfect solution. The Domain Class has 2 functions that are there to support a non domain requirement. The Memento class could also be sub-classed, and an instance could be used to build the Domain Class (but why? It's much more simpler to build it with the default constructor). But, except this small amount of pollution, the domain stays really clean and I can really concentrate on the domain requirement, without thinking how to manage the persistence.