Big Blue Series — 14 — Powerfully Flexible Functionality By Design
Imagine building a kit car to spec all the way down to a glossy coat of paint only to find out a critical piece of the motor, a small pin, was left out. The tech at customer service gives you the bad news, you need to remove the engine, disassemble it, and start over.
Whew. Welcome to the life of many programmers trying to make a massive system work that has far too many parts, too many complex connections and to much all glued together in one place.
Domain-Driven Design (DDD), as described in the book I call “Blue” (Evans, 2004), is a paradigm to make life easier for the people who must enhance, repair, and otherwise improve the software that is critical to the organization.
So far, we have described some ingredients that are used in making a Domain Model. Specifically, the Entity and the Value Object. Entity objects have identity, and Value Objects require no tracking of identity. Incidentally, it costs less to maintain a Value Object overall for that reason. But I digress.
Anemia Is a Common Weakness
One big inefficiency and poor design we see in the wild is this notion of treating the objects we are talking about as boxes of data. This is not generally how developers are taught to code by savvy institutions and skilled developers. It is, though, how some have drifted in their craft. It is called the “Anemic Object Model” because there is not really enough functionality in the objects themselves to call that model “healthy”. We want rich objects not poor ones if we want a powerfully flexible system.
These objects, for most organizational needs, should not just be boxes that hold data parts. To be of much more use, they should also hold functionality parts right inside the Entity or Value Object that should own that functionality.
This is where many developers struggle. The challenge is knowing what Entity or Value Object should be doing the various types of work the software is being asked to do. Many give up and just create a new functional object just for that capability. It is not a wrong move in some cases, but that should not be the default solution.
Services
What if the functionality belongs in this system, but it does not belong on any Entity or Value Object? In fact, it is recommended to make a separate functional object called a Service when there is no real good place to hold that functionality in an Entity or Value Object. And even then, it is a good idea to reevaluate those Services from time to time to be sure they still deserve to be unowned by another object.
Digging Deeper to Wag The Tail Better
When developers learn to code, they are often taught to make an Animal object that can be absorbed by a Dog and a Cat object so that all universal Animal features can be coded one time in the Animal object. The Dog and the Cat generally have tails, but not all dogs and cats have tails. So, they learn to decorate each animal with a tail object only where applicable.
So far, we are just talking about things (Animal, Dog, Cat, Tail). What about the action of wagging said tail if it exists? Do you put the wag on the dog so that it wags a tail, or do you let the tail, if it is there, own the wag functionality along with all the instructions that say how much, and in which direction the tail should wag and when to stop? What if there are many ways one dog might wag a tail, how do we let the dog, or the tail know what type of wagging is supposed to happen?
Dog.WagMyTail(speed int, wagPattern int)
{Do the wagging here}
OR
Dog.tail.StartWagging(400rpm, Propeller))Tail
- StartWagging(speed int, wagPattern int)
{Do the wagging here}
- StopWagging()
It gets interesting the more you develop this model. It is a fun and challenging process to put the right names on the right things and then give those things the power to operate the way they need to.
Fueling Flexibility
It gets complicated and Blue encourages us to break this down and then test several model options. Even if we get it working and find out later there needs to be a better way, because we always do, then we can move that functionality into a more appropriate slot. This works, because the objects are full of functionality already and now, we are just moving it to a better place. If all this capability were not attached to the object that should own it, then we have a bit of a spaghetti code problem when trying to figure out what will happen when.
So, if the model is rich with data and functionality, then adjustments are easier. It just requires a little testing of a newly updated model and then roll with the new best design if appropriate.
Not Right or Wrong
Enough about Animals. In the corporate world, these can be interesting questions. Customers have Orders. Orders need to be given instructions to go do things. How much of that is in the Customer, or the Order, or should be in a Service?
The fact is, it may not matter too much in the beginning. You can try one way and if it works and there is no need to update, roll with it. On the other hand, nuances may come into play when new functionality needs to enter the scene and the current action to be updated is in the wrong object getting the update. The name of the game is not about getting right but rather about getting it powerfully flexible. That is how real agility works in software.
So that rounds out the three types of building blocks we find in Blue, Entities, Value Objects and Services. The one item that was not there is next. Events.
Until then…
I hope I see you in the next installment of — the Big Blue Series.
References
Evans, E. (2004). Domain-Driven Design: Tackling Complexity in the heart of software. Addison-Wesley Professional