I’ve been watching Ted M. Young’s videos for a while now. Each one has a clear, concise message: Design your types well. This is somewhat unusual. Most object-oriented and code smell advice focuses on structure and coupling.
But Ted is quite pragmatic and straight-forward about the need to have clear types in a typed Object-oriented language. Rather than badly designed objects and classes, he speaks about domain-free types and business-centrism. This ties in perfectly to topics that we here at Crafting Tech Teams explore often: Domain-driven Design, Clean/Hexagonal architecture.
The Elephant in the Room: Primitive Obsession
stipulates what these types have in common is that they are Domain-free. They don’t tell much about your domain. When used in a domain layer (a la DDD or Clean Architecture) they need to be accompanied by surrogate properties to fully capture what they mean. And meaning isn’t the whole picture. You also want to deal with state and the business invariants (rules) that they relate to.The answer to this is encapsulation. But how do we tell good encapsulation from bad ones? The downstream consequence of primitive obsession is another code smell called feature envy.
Feature envy is an anti-pattern where 2 objects cooperate with inappropriate coupling to perform a single action: one has the behavior of a method, the other one has the data it needs. This is the most common smell I have observed, especially in projects striving to be object-oriented but can’t quite hit the mark.
Ted has a great example and checklist on how to refactor this. The solution is obvious: create more, smaller types that enrich the primitives to the point where they can absorb the method of behavior to where the data is.
This carries an immediate, but perhaps unseen benefit: data is being split up so it follows usage and mutation scenarios, not storage ones. Ted calls this database-centrism and is a common plight among architects. You could say the reason processes such as OO, Clean Architecture and Hexagonal exist is to give us better alternatives to database centrism.
Ted’s Refactoring Checklist
For scalar primitives
This also has the benefit of converting your getters to queries, returning “logic upon data” vs. the data itself.
Fix feature envy—gather behavior close to the data, removing getters/setters
Remove getter & setter via Inline Method.
Create new Type—Extract the methods and data they need to a new class. This is the 1st-level logical: invariants of the Type.
Create getter/setter for new Class in old file.
Change the old scalars to the new type
This re-introduces feature envy, but combines many calls to a single one.Fix the last feature envy—Gather the usages of the new type and move them to where they are needed. This is the 2nd-level logic: how the Type is used.
Remove the setters and getters
For collection primitives
Similar to the scalar. We want to move the code that directly uses the collection and extract that behavior to a new Type, including the data of the collection.
Extract class for the primitive collection with Extract Class (Delegate)
Identify usages of this new getter
Move them to the new class one by one
Repeat until class is complete
Check for feature envy or lack of parameters
Original video
Ted’s upcoming course on Testing w/ Hexagonal Architecture
The hallmark benefit of good design is being able to test it easily. Our industry is relatively young and the majority of engineers have less than 5 years of experience. It’s commonplace to find large legacy codebases backing profitable businesses that have poor design and little-to-no testing. Refactoring, hexagonal and clean architecture are all good tools on how to clean such a project.
We discussed such topics on last month’s live streams and Ted is preparing a course on it that’s launching in Sep/Oct 2023. Do check it out and give Ted a follow! I’ll be reviewing the course first chance I get.
Focus on the Business Domain, avoid database-centrism
If you follow the above refactoring checklists in inverse order, you will quickly come to the anti-pattern side of our spectrum: Database centrism.
In a Database centric application, the data from the DB gets stuck in infrastructure-layer concerns like ORM-generated classes acting as data holders. They are maximally separated from domain-layer concerns like features, functionality and invariants.
I’m sure you have seen such an application architecture. You or your teams may be working on such a project right now.
As you may notice, there are strong parallels between good design, pure object-oriented concepts, functional programming, architecture and clean code.
Domain-free vs. Domain-rich
Good design starts with good questions. However, your codebase may not be in a state to handle the answers. Good tactical OO and DDD design is key to facilitate—and even drive—strategic domain conversations.
After all, you cannot follow the checklist above to create new types if you don’t know the business domain of the application you are maintaining.
Over the years I have found quite a few good resources to tackle pure object orientation in absence of the domain aspects. Purely tactical, OO design:
MicroObjects by Quinn Gill
💬 Join the discussion
I live stream every Thursday at 16:30 CEST for an hour. Our current theme is Domain-driven Design following its 20-year anniversary. Come ask, learn, have a contrary view, let’s disagree and discuss. The discussion is live and I’ll take your questions on the spot!
Subscribe to the newsletter to stay tuned, I post recaps and announcements every Tuesday. Share with your friends to spread the fun.