Rich Domain Objects

software-engineering
Author

Humberto C Marchezi

Published

March 19, 2008

In object oriented programming, domain objects are the key of the software development. However, many programmers tend to write these classes as simple get/set storage just like the example below in pseudo-code:

class Client
{
    private long id;
    public string Id
    {
        get { return this.id; }
        set { this.id = value }
    }
    private string name;
    public string Name
    {
        get {return this.name; }
        set { this.name = value; }
    }

    private int registrationYear;
    public int RegistrationYear
    {
        get { return this.registrationyear; }
        set { registrationYear = value; }
    }
}

The instances of the class above are the so-called anemic-objects. These objects don’t verify their internal state and their behavior and thus accept any value as input.
As a consequence the extra-work is delegated to the application.

However these objects are not very practical for complex domain models and they don’t take full power of the object programming. Classes not only carry data but also a functional part which are exactly the methods and properties.

To attack complex domains there are rich domain objects which are objects capable of verifying all these aspects internally preventing the programmers from having to remember them later at the time of building the application.

Rich domain objects help other developers not familiar with the business rules on how to create an application for that particular domain.

In order to work efficiently with rich domain objects the following design rules can be adopted:

// Rule 1 - Constructor with mandatory parameters
public Client(name,registrationDate) {...}
// Rule 2 - Use properties instead of fields
{ this.Name = name; this.RegistrationDate = registrationDate; }
public string Name
{
    get { return this.name; }
    // Rule 3 - Set property checks the input value
    set { 
        if (value == "") throw new SystemException("Empty name is invalid");
        this.name = value;
    }
}
public int RegistrationYear
{
    //Rule 4-Get property checks object and application state before returning a value
    get
    {
        if (User.CurrentUser().IsManager()) return this.registrationYear;
        else throw new SystemException("No permission for Client's Registration Year.");
    }
    set
    {
        if ((value <> DateTime.now.year)) throw new SystemException("Year must be from 1980 until now");
        this.registrationYear = value;
    }
}

Following this rules a Client class could be:

// Rule 5 - Client Class is designed according to the business model
class Client
{
// Rule 1 - Constructor with mandatory parameters
public Client(name,registrationDate)
    // Rule 2 - Use properties instead of fields
    { this.Name = name; this.RegistrationDate = registrationDate; }

    private string id;
    public string Id
    {
        get { return this.id; }
        set { this.id = value; }
    }

    private string name;
    public string Name
    {
        get {return this.name; }
        // Rule 3 - Set property checks the input value
        set
        {
            if (value == "") throw new SystemException("Empty name is invalid");
            this.name = value;
        }
    }

    private int registrationYear;
    public int RegistrationYear
    // Rule 4-Get property checks object and application state before returning a value
    {
        get
        {
            if (User.CurrentUser().IsManager()) return this.registrationYear;
            else throw new SystemException("No permission for Client's Registration Year.");
        }
        set
        {
            if ((value != DateTime.now.year)) throw new SystemException("Year must be from 1980 until now");
            this.registrationYear = value;
        }
    }
}