(I know I said I was going to go over configuring your R Pi as a dev machine but the videos are taking longer than I suspected. In the mean time, I decided to do a quick post on getting started with domain driven design. Enjoy!)

Two of the most important Domain Driven Design object modeling concepts are Entity Types (Reference Objects) and Value Objects. Unfortunately, they and can also be two of the most confusing. As an engineer modeling a problem domain, are you sure you should be creating a value object or should your model be an entity reference type? Differentiating the two is difficult even before introducing more advanced concepts such as reference objects, value objects, immutability, factories, entity identity, object comparison, etc. To avoid a full blown discussion of DDD topics, let’s put all of that aside for the time being and explore some simple concepts I’ve found useful when modeling objects in any problem domain.

These are not best practices but some ideas to improve your object design. Remember, the overall goal is convey the best model of your domain as possible and only allow the model to function as intended. When in doubt, K.I.S.S. - Keep It Simple Stupid. Please feel to leave a comment or any suggestions and you can find the full source for the examples below in my Github project called DDDHotList

1. Implement Least Visibility Principle

  • Gradually reveal to external world as necessary
  • Keep properties at lowest visibility before exposing them: private fields > public properties
  • Use private fields
  • Use readonly and const variables

2. Do not implement setters on public properties!

yoda_setters_meme.webp

Visual Studio is a great tool but sometimes it promotes bad design. Case and point is the prop shortcut (type “prop” into Visual Studio or Visual Studio code class definition and the IDE will create a new property with both a getter and a setter). More often than not domain models do not need public setters on most of their properties. In these instances, it is best to use the constructor to inject property values on object instantiation.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
/// <summary>
/// A dirty unorganized house. 
/// 
/// Note: The openess at which public setters are used. Unless you are
/// absolutely sure this is the design you need, it should be avoided.
/// 99% of the time this is not needed
/// </summary>
public class DirtyHouse
{
    public int Windows  { get; set; }
    
    public int Doors { get; set; }
    
    public string Color { get; set; }
}

/// <summary>
/// A standard house. 
/// 
/// Note: Notice the private setter methods. Private setters are 
/// a significant design improvement over fully open setter methods. If you need to
/// Access a method sparingly, use custom setter functions. Only make the setter property 
/// public as a last resort.
/// 
/// Note: Because the properties have private setters, you must construct the object
/// using the constructor.
/// </summary>
public class StandardHouse
{
    public StandardHouse(int windows, int doors, string color)
    {
        Windows = windows;
        Doors = doors;
        Color = color;
    }

    public int Windows {get; private set;}
    
    /// <summary>
    /// Customer setter method for setting the number of windows in the house
    /// </summary>
    /// <param name="windows"></param>
    
    public void SetWindows(int windows){
        Windows = windows;
    }
    
    public int Doors {get; private set;}
    
    public string Color {get; private set;}
}

3. Readonly properties are better than private properties!

What’s better than domain model properties with private setters? Domain model properties that are readonly! Short of using the reflection APIs and Dark Sidious Force Lightning, properties that are marked as readonly can only be modified in their constructors. This keyword works wonders for object modeling and allows engineers to make domain models all but immutable after they have been created.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/// <summary>
/// A clean house.
/// 
/// Note: The use of readonly publicly facing methods. This means 
/// you can reason about the object and be certain it has not been modified
/// since it was created.
/// </summary>
public class CleanHouse
{
    public CleanHouse(int windows, int doors, string color)
    {
        Windows = windows;
        Doors = doors;
        Color = color;    
    }

    public readonly int Windows;
    
    public readonly int Doors;
    
    public readonly string Color;
}

4. Use static “Factory” methods on domain models for common model creation scenarios

Although you generally want to shy away from the use of static methods, they can be extremely useful as simple factory methods for instantiating popular object models in your domain. If you are going to be new’ing up the object anyways, use a simple static method that lives on the object is not a bad way of simplifying and consolidating the object instantiation. Be careful not to add too much code to your statics factory methods and be sure to place them directly on the model object.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
/// <summary>
/// A mansion object.
/// 
/// Note: "Factory" style object creation. We are using static methods to standardize the creation
/// of certain types of objects. This is combined with private constructors as a powerful technique
/// to insure your objects are created correctly and safely
/// </summary>
public class Mansion 
{
    /// <summary>
    /// Create a new mansion with a pool
    /// </summary>
    /// <param name="windows"></param>
    /// <param name="doors"></param>
    /// <param name="color"></param>
    /// <returns></returns>
    public static Mansion withPool(int windows, int doors, string color)
    {
        return new Mansion(windows, doors, color, true);
    }

    /// <summary>
    /// Create a new mansion without a pool
    /// </summary>
    /// <param name="windows"></param>
    /// <param name="doors"></param>
    /// <param name="color"></param>
    /// <returns></returns>
    public static Mansion withoutPool(int windows, int doors, string color)
    {
        return new Mansion(windows, doors, color);
    }

    private Mansion(int windows, int doors, string color, bool hasPool = false, IEnumerable<string> roomNames = null)
    {
        Windows = windows;
        Doors = doors;
        Color = color;
        HasPool = hasPool;
        RoomNames = roomNames;
    }

    /// <summary>
    /// Convert a mansion to a clean house
    /// </summary>
    /// <returns></returns>
    public CleanHouse CleanHouse()
    {
        return new CleanHouse(Windows, Doors, Color);
    }


    /// <summary>
    /// Use of IEnumerable means that object cannot be modified inadvertently by typical add/delete/sort
    /// collection operations but still allows object to be iterated over in an foreach loop
    /// </summary>
    public readonly IEnumerable<string> RoomNames;

    public readonly int Windows;

    public readonly int Doors;

    public readonly string Color;

    public readonly bool HasPool;

    public void ShowHouse()
    {
        if(HasPool)
        {
            Console.WriteLine("Mansion with Pool:");
        }
        else
        {
            Console.WriteLine("Mansion without Pool:");
        }

        Console.WriteLine($"    Color: {Color}");
        Console.WriteLine($"    Windows: {Windows}");
        Console.WriteLine($"    Doors: {Doors}");
    }
}

5. Use private constructors!

Consider private constructors to consolidate static factory methods for object creation. This method of object creation is useful when creating objects that have more complex options required for instantiation. At some point, if your model object because too complex, I suggest creating a full blown object factory. Just as with the public static factory methods, remember not to add too much code to your private constructor objects.

Refer to 4. above for example

6. Use IEnumerable for collection properties!

IEnumerable protects your list collection from an unintended operations. More often than not, list properties only need to be iterated over and do not need to be added to or sorted. In this case, IEnumerable list properties get the job done and protect future engineers from misusing domain objects.

Refer to 4. above for example

7. Use private classes for internal modeling

As private classes will only be available internal to the class they are declared, they are fantastic for reducing complexity and increasing organization within a domain object. Private classes are heavily under utilized but can work wonders for modeling domain object internals.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
/// <summary>
/// Private class to handle shifting. This class takes engine rpms and 
/// </summary>
private class Gear
{
    public Gear(int minRpms, int maxRpms, int speed)
    {
        _minRpms = minRpms;
        _maxRpms = maxRpms;
        Speed = speed;
    }

    /// <summary>
    /// The minimum number of rpms in this gear range
    /// </summary>
    private readonly int _minRpms;
    
    /// <summary>
    /// The maximum number of rpms in this gear range
    /// </summary>
    private readonly int _maxRpms;
    
    /// <summary>
    /// The speed produced by the gear
    /// </summary>
    public readonly int Speed;
    
    /// <summary>
    /// Determine if the gear is in range
    /// </summary>
    /// <param name="engineRpms"></param>
    /// <returns></returns>
    public bool isInRange(int engineRpms)
    {
        if (engineRpms >= _minRpms && engineRpms <= _maxRpms)
        {
            return true;
        }
        return false;
    }
}

8. Use protected classes for internal object modeling within an inheritance structure

Protected classes enable you to share object structures found within base classes while simultaneously guarding outside objects from manipulations. They are a great way to decrease complexity of internal implementations.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
/// <summary>
/// Class modeling the transmission of a motor vehicle
/// </summary>
protected class Transmission
{
    private readonly Gear _firstGear;

    private readonly Gear _secondGear;
    
    public Transmission(int firstGearRpms, int secondGearRpms)
    {
        _firstGear = new Gear(100, firstGearRpms, 10);
        _secondGear = new Gear(firstGearRpms + 1, secondGearRpms, 30);
    }
    
    /// <summary>
    /// Adjust the speed of the car based on the engine rpms
    /// </summary>
    /// <param name="engineRpms"></param>
    /// <returns></returns>
    public int TransferEnergy(int engineRpms)
    {
        if (_firstGear.isInRange(engineRpms))
        {
            return _firstGear.Speed;
        }
        else if (_secondGear.isInRange(engineRpms))
        {
            return _secondGear.Speed;
        }
        return 5;
    }
}

You can find the full source in my Github account project DDDHotList

Enjoy and happy coding! Comments? Questions? Suggestions? Leave them below!

Resources

Domain Driven Design by Eric Evans (The book that started it all)

Design Patterns: Elements of Resusable Object-Orientated Software (Classic Gang of 4 book)


Gravatar Image

Brian Mikinski

Software Architect - Houston, Tx