Well I am trying to apply domain driven design principles for my application, with a rich domain model that contains both data fields and business logic. I've read many DDD books, but it seems that their domain models(called entities) are very simple. It becomes a problem when I have a domain model with 10-15 data fields, such as the one below:

class Job extends DomainModel{

    protected int id;
    protected User employer;
    protected string position;
    protected string industry;
    protected string requirements;    
    protected string responsibilities;    
    protected string benefits;
    protected int vacancy;
    protected Money salary;
    protected DateTime datePosted;
    protected DateTime dateStarting;
    protected Interval duration;   
    protected String status;
    protected float rating;  

    //business logic below 
}

As you see, this domain model contains a lot of data fields, and all of them are important and cannot be stripped away. I know that a good rich domain model should not contain setter methods, but rather pass its data to constructor, and mutate states using business logic. However, for the above domain model, I cannot pass everything to the constructor, as it will lead to 15+ parameters in constructor method. A method should not contain more than 6-7 parameters, dont you think?

So what can I do to deal with a domain model with a lot of data fields? Should I try to decompose it? If so, how? Or maybe, I should just use a Builder class or reflection to initialize its properties upon instantiation so I wont pollute the constructor with so many arguments? Can anyone give some advice? Thanks.

2 Answers

9
theDmi On Best Solutions

What you've missed is the concept of a Value Object. Value objects are small, immutable objects with meaning in the respective domain.

I don't know the specifics of your domain, but looking at your Job entity, there could be a value object JobDescription that looks like this:

class JobDescription {
    public JobDescription(string position, string requirements, string responsibilities) {
        Position = position;
        Requirements = requirements;
        Responsibilities = responsibilities;
    }

    public string Position {get;}
    public string Requirements {get;}
    public string Responsibilities {get;}
}

This is C# code, but I think the idea should be clear regardless of the language you are using.

The basic idea is to group values in a way that makes sense in the respective domain. This means of course that value objects can also contain other value objects.

You should also ensure that value objects are compared by value instead of by reference, e.g. by implementing IEquatable<T> in C#.

If you refactor your code with this approach, you will get fewer fields on your entity, so using constructor injection (which is highly recommended) becomes feasible again.


Further notes regarding your example code that are not directly connected to the question:

  • The domain model is the whole thing, an entity is part of it. So your base class should be called Entity and not DomainModel.

  • You should make the fields of your class private and provide protected accessors where required to maintain encapsulation.

2
Matt On

There's an awful lot going on in your Job domain model object - it seems to mix a huge number of concerns, and (to me at least) suggests a number of bounded contexts, some of which are easy to discern for the sake of making an example.

  1. Remuneration (pay, benefits)
  2. Organisational position (reporting line)
  3. Person spec (skills)
  4. Job specification (responsibilities)
  5. etc.

When you consider the things that interact with your 'Job' model, are there any that need to inspect or mutate BOTH the Salary property and the Industry property, for example?

Without knowing the full nuances of the domain, the Salary you get for holding a position and the Industry you work in are not really connected, are they? Not a rhetorical point; these are the questions you NEED to ask the domain experts.

If they DON'T have any interaction then you have identified that these two things exist in two different BOUNDED CONTEXTS. The Salary side has no need of any interaction with the Industry side and vice versa, and even if they did, do they need to be held as state in the same process at the same time?

Think about the lifecycle of how a person becomes an employee; a person applies for a job. The job has a specification, salary range. The person attends an interview. The hirers offer the person the position. The person accepts. The person is now an employee, not a candidate any longer. The new employee is now accruing holiday and benefits and has a start date etc.

DDD teaches us that a single, unified view of the world rarely serves ANY of the concerns correctly. Please explore BOUNDED CONTEXTS - your software will be much more pliable and flexible as a result.