Merry Christmas Sale! 🎄 Ends in 6 days.
Friends, what an incredible year it has been. If I know us well, we're always moving forward. We're following our curiosity. And most importantly, we're growing.
Never forget one of the most important success techniques there is: reflection. So, take a moment to look back on how far you've come this year. Go on and collect the lessons, get inspired, and prepare for a prosperous 2025.
Before December 29th, you can get 50% off of Testing Mastery using the discount code XMAS-50 and 10% off the entire suite of courses in the academy using the discount code TSE-XMAS.
Thank you, and may 2025 grant you the growth, love & power to create what matters most in your world
Enjoy your holidays. See you next year.
And always, To Mastery!
Object Stereotypes
🌱 This blog post hasn't fully bloomed. It's very likely to change over the next little while.
Why the heck is LEGO still so comforting after all these years?
Maybe it's because it feels good to come back to the same baseline elements: the brick, the baseplate, the tile, the mini-fig, etc.
Object stereotypes, a concept from Responsibility-Driven Design, are effectively your programming LEGO.
They’re your starting pieces when you're trying to find the right abstractions to create.
Within your codebase, your libraries, frameworks, and so on - if you look closely, you'll notice that your objects tend to conform to one or more object stereotypes.
How to use object stereotypes
The Responsibility-Driven process involves taking a requirement, extracting out the responsibilities, and the assigning those responsibilities to roles.
How do you find the correct role?
Look to the stereotypes.
They act as hints to the roles we want to introduce.
The 6 Stereotypes
- The Information Holder
- The Structurer
- The Service Provider
- The Coordinator
- The Controller
- The Interfacer
1. The Information Holder
The Information Holder is perhaps the simplest stereotype we know of. Most similar to idea of a Value Object made popular in Domain-Driven Design, Information Holders are usually domain layer concerns, but they can also represent other things as well (such as Data Transfer Objects).
What’s important to know is that the stereotypical responsibility of this role is to hold information. Anytime you notice responsibilities to hold info, consider designing that object candidate as an Information Holder.
2. The Structurer
Structurers are primarily about creating and maintaining relationships between objects. Structurers protect against invariants and can make it easy to find related objects.
Examples of Structurers are Aggregates (from Domain-Driven Design), hash tables (or any mechanism responsible for caching), or objects that pool or handle connections.
3. The Service Provider
Service providers are objects that sit passive and contain useful, specialized computations.
Stable service providers: They typically sit dormant waiting for other objects to call them, but because they’re often relied upon by so many objects, they may contain responsibilities that relatively stable, not changing often.
For example, utility classes like TextUtil, DateUtil, NumberUtil provide respective text, date, and number utilities and probably don’t change dramatically (or at all for that matter). For this reason, we’d say they’re stable. Just think about what would happen if we had to change the JSON.stringify
method in JavaScript.
Pure fabrication: Alternatively, service providers are also objects that live in the domain layer yet don’t represent concepts from the problem domain. Instead, they’re objects that are specifically invented to ensure other domain objects maintain low coupling, high cohesion, and encapsulation. In Domain-Driven Design, we call these types of classes Domain Services.
Cross-cutting concern service providers: Lastly, service providers are known to contain responsibilities that are commonly know as “cross-cutting concerns” like logging, security, or tracing (each of which are non-functional requirements). A concern = a functional or non-functional requirement. Non-functional requirements are the ones that are “cross-cut”. This means that Use Cases either rely on these service-providers OR we design the collaboration so that the service providers wrap the Use Case (hence the name “cross-cutting”).
Either solution works, but there are tradeoffs to both.
4. The Coordinator
The Coordinator stereotype has one real purpose: to pass information to other objects so that they can do work. When we look at it like this, Coordinators are one of two objects most responsible for determining how control happens in our applications (the other is the Controller).
5. The Controller
We’ve dealt with this particular type of stereotype before, but we’ve been calling it the Use Case instead. In RDD, the Controller stereotype is the exact same thing as the Use Case pattern.
The stereotypical responsibility held by a Controller is to act as a higher-level (application-layer specifically), declaratively focused object that coordinates the behaviour of lower-level concerns.
6. The Interfacer
The last of the stereotypes is the Interfacer. The Interfacer primary acts as a bridge between object neighborhoods (internal and external).
Internal interfacer: To cross internal object neighborhoods, we need to make the entry-point for our components as simple as possible. The object which sits at the front, relaying messages to objects behind the scenes is the Interfacer.
External interfacer (gateway): To cross external object neighborhoods (as is the case when we send messages out to remote APIs or to libraries which we don’t own), the more common name for the object that encapsulates this access is called a Gateway, however — stereotypically, it’s an Interfacer.
UI-related interfacers: Interfacers which listen to events from the UI and pass them to non-UI related parts of our applications are typically just called Event Listeners (in JavaScript, there is an EventListener API) whereas objects that format data for presentation in the UI are aptly named Presenters. Both are stereotypical Interfacers.
Considerations on stereotypes
Stereotypes make it easier for us to identify objects: This should clear up a lot of headroom for you. Because we know the six general types of objects we’re likely to encounter, we'll find it easier to invent object candidates.
Stereotypes help us emphasize what matters about our objects: We’re constantly striving to ensure that our objects are cohesive and well-defined. If we discover a new object candidate, we can ask ourselves “what stereotype is this most like?” This question helps us determine if we should redistribute misplaced responsibilities to other object candidates to make them more cohesive and focused.
A candidate object can belong to more than one stereotype: Taking it the other direction, there are times when objects have responsibilities that float between multiple stereotypes. For example, consider the Repository pattern. If it were to also cache the objects it retrieves, we’d say it acts more like a combination of the Interfacer and Structurer stereotypes.
Object stereotypes make understanding & communication simpler: It’s easier to talk about design with stereotypes in mind. Once you’re aware of object stereotypes, you’ll start seeing them (or the lack of them) in the frameworks and libraries you’re using. This presents a great opportunity for us to get what we need by creating it ourselves.
Stay in touch!
Join 20000+ value-creating Software Essentialists getting actionable advice on how to master what matters each week. đź––
View more in Object-Oriented Design