Manning Up to Your Model
Announcer: From the EIP Chipotle Operations Center in East County, San Diego, it’s time for another thrilled-packed excursion into Programming Excellence on I Built His Cage!
Well, even the celebratory bliss of the 2009 AFC West Champions can’t tear me away from you, loyal reader, and a Christmas Eve Eve post.
One of my favorite programmer cartoons is Developer: Born Brave.

I suppose the main thing this cartoon gets at is that we programmers are the ones brave enough to face the horrible details of telling a complete dummy exactly how to do something, and–worse–troubleshooting and fixing when we screw it up.
This is one type of bravery. Now that I am in the midst of my second proprietary, and first since I started this blog, attempt at domain-driven design, not only am I infinitely wiser this time around, but I am noticing a second kind of bravery that is necessary if you are looking to utilize DDD beyond the set of patterns it advocates.
This sort of bravery goes beyond most small-scale refactorings like Extract Method or Move Field, and probably also the large scale ones, like Replace Conditional With Polymorphism. Rather, this sort of bravery involves re-working your design to match the problem domain, and a great deal of this involves the courage to realize that your code might work and might be beautiful, but it just isn’t quite… right.
Evans called it “Refactoring Towards Deeper Insight“. I call it “Manning Up to Your Model”.
In order to do this, you have to have courage in your convictions that the insight will help you later when it comes time to implement new functionality and explore another part of the problem domain. It is all the more baller if you have a partner who is on board; especially if you’re in charge, which is just…so great.
You need to stand up (often times) against project managers who don’t see the point of what you’re doing. Like most refactoring, this does not add functionality, but unlike most refactoring, neither does it clean the code up significantly. Rather, taking these steps lessens the impedance mismatch between the developers and the business.
The intractability of this benefit necessitates much of the guts required. You’ll need even more balls if your project manager actually views a decoupling of the code and the domain as a benefit.
Here are three such gut-check moments I have had on my second excursion into domain-driven design.
The case of the if-statements
Ahh yes, the if-statement, everybody’s favorite retarded racoon. When creating a certain entity object, let’s say Equipment, we had to look up certain pieces of data in the database. During testing, we discovered that what the UI sent us had to be massaged before we looked it up.
Consider the following:
Equiment create(EquipmentCreationRequest request)
{
// TransAtlantic origin
if(request.aeID().equals("12") &&
request.trackingNumber().contains("001"))
{
request.setAeID("00");
request.setTrackingNumber("00");
}
// North-south shipment
else if(request.aeID().equals("442") &&
request.trackingNumber().equals("28"))
{
request.setForTaID("00");
}
// default
else
{
request.setHRoute("None");
}
EquipmentData data = STATIC_MAP.get(request);
return new Equipment(data);
}
The point here is not to recoil in obvious disgust for the technical stank of this code. The point is that our business experts aren’t thinking in terms of if-statements. Both the developers and business experts were saying “well we need to filter out any equipment requests whose AEIDs are 12 and the tracking number has a 001″. After such a conversation, we had to translate that into what it looked like in code.
Now consider this:
abstract class EquipmentCreationFilter
{
private EquipmentCreationRequest request;
abstract boolean isApplicable();
abstract void apply();
EquipmentCreationFilter(
EquipmentCreationRequest request)
{
this.request = request;
}
EquipmentCreationRequest getRequest()
{
return this.request;
}
}
class TransAtlanticCreationFilter extends EquipmentCreationFilter
{
TransAtlanticCreationFilter(
EquipmentCreationRequest request)
{
super(request);
}
boolean isApplicable()
{
return
this.request.aeID().equals("12") &&
this.request.trackingNumber().contains("001");
}
void apply()
{
this.request.setAeID("00");
this.request.setTrackingNumber("00");
}
}
Equiment create(EquipmentCreationRequest request)
{
EquipmentCreationFilter filters = getFilters(request);
EquipmentCreationRequest filteredRequest = EquipmentCreationRequest.NULL;
boolean aFilterWasNotApplied = true;
for(EquipmentCreationFilter filter : filters)
if(filter.isApplicable())
{
filter.apply();
filteredRequest = filter.getRequest();
aFilterWasNotApplied = false;
}
// default
if(aFilterWasNotApplied)
request.setHRoute("None");
EquipmentData data = STATIC_MAP.get(filteredRequest);
return new Equipment(data);
}
private Collection<EquipmentCreationFilter>
getFilters(EquipmentCreationRequest request)
{
Collection<EquipmentCreationFilter> filters =
new ArrayList<EquipmentCreationFilter>();
filters.add(new TransAtlanticCreationFilter(request));
filters.add(new NorthSouthCreationFilter(request));
return filters;
}
Now both groups of people are on the same page. A project manager might understand your need to constrain cyclomatic complexity in the future, but all of those conversations with the domain expert need to be defended in the name of Deeper Insight and a Ubiquitous Language.
The case of the decimated tree
That wasn’t as drastic an example because the code stunk in both technical and domain ways. You really need to straighten your spine when the code works, your code is clean, and you still want to change it because the model doesn’t make sense anymore.
Continuing from the previous example: the data we were looking up to create Equipment objects appeared to be structured as a tree, but it was represented in the database as a flat, denormalized set of rows. Since we needed this data for purposes other than creating objects, we naturally wrote code to build a nice, navigable, and sensible data structure for our consumption.
Based on the initial load of the table, we observed that an Indicator contained one or more Lifecycle States, and each Lifecycle State one or more, for lack of a better term, Data Groups. A Data Group was TaID, AEID, and Tracking Number together. What’s more is that we believed that Lifecycle States could be shared between Indicators.
Here is what it looked like:
| Indicator 1 | Status 1 | 12 | 5 | 001 |
| Indicator 1 | Status 2 | 14 | 4 | 199 |
| Indicator 2 | Status 2 | 33 | 19 | 9 |
| Indicator 2 | Status 3 | 188 | 3 | 4 |
So we wrote some nice, working, clean code that built a tree like this:

In fact, the STATIC_MAP referenced in the earlier example was a map from Data Group to Lifecycle State.
Life was good until new requirements were discovered and a new row had to be added to the table:
| Indicator 1 | Status 1 | 12 | 5 | 001 |
| Indicator 1 | Status 2 | 14 | 4 | 199 |
| Indicator 2 | Status 2 | 33 | 19 | 9 |
| Indicator 2 | Status 3 | 188 | 3 | 4 |
| Indicator 1 | Status 1 | 188 | 3 | 4 |
Now what? Technically, the code that built the tree still worked:

But now the code that consumed it and mapped Data Group (the three numbers) to Lifecycle State was broken. In this case, {188,3,4} maps to both Status 1 and Status 3. These three values no longer uniquely identified a Lifecycle State.
It turned out that the three values plus the Indicator uniquely identified a Lifecycle State. It felt a little strange, but it was easy enough to modify the code that consumed this data structure to fix the problem.
Until another row was added:
| Indicator 1 | Status 1 | 12 | 5 | 001 |
| Indicator 1 | Status 2 | 14 | 4 | 199 |
| Indicator 2 | Status 2 | 33 | 19 | 9 |
| Indicator 2 | Status 3 | 188 | 3 | 4 |
| Indicator 1 | Status 1 | 188 | 3 | 4 |
| Indicator 2 | Status 2 | 12 | 5 | 001 |
Can you spot the problem now?

If you look closely, when consumed, the tree indicates that {Indicator 1,12,5,001} maps to Status 2 when no such association exists. Now changing the consuming code didn’t save us. The tree itself was flawed. Clearly, Lifecycle States can’t be shared between Indicators.
The fix now renders the tree like this:

This worked but felt off because in our problem domain, there was only one Status 2, not two of them. The expressiveness of the Lifecycle States was diminished by our now-faulty model that this data structure was a tree rooted at Indicators.
Furthermore, our requirements evolved to the point where we didn’t need Indicators at the root anymore. The more we talked about the relationship between Indicators and Lifecycle States with our domain expert, the more we realized that Lifecycle States subsumed Indicators, not the other way around.
Thus, we followed our noses and refactored towards Deeper Insight:

After updating the consumers of this new tree appropriately, we ended up with working, clean code that also lined up with our domain model in a way the older version simply didn’t.
The case of the double agent
This example also features a bug fix that worked and was clean, but deviated too far from the concepts of our problem.
Every piece of Equipment had a set of associated Export IDs and an Export Name for all of those Export IDs. Or so we thought. Upon reading the fine print of the stored procedure’s tech spec, I realized I wrote the data access code incorrectly.
An Equipment had one or more Export Names, and for each of those Export Names, there was a set of Export IDs. Great.
The short-term fix was to create a new Equipment object for every Export Name we came across in the result set. Since Equipment is an entity, this was a terrible thing because now we had to deal with the possibility of Equipment with ID = 123 out there twice, once for Export Name “Sally” and another for “Harry”. Not good. It’s just…so horrible.
We wised up and updated the Equipment object model such that there was one and only one object per ID. Each Equipment could answer questions about which Export IDs applied for a given Export Name.
You know you’re right
Refactoring for technical cleanliness is one thing. Manning up to your model is for developers born brave.
![]() |
Announcer: You’re reading the EIP web-ring. |

March 4, 2010 at 11:10 pm
http://videosterilisation.blogspot.com/2010/03/uncreated-lumpier-video.html
funny funny top video top.
March 5, 2010 at 11:34 pm
free
video download videos free fun.