Objects Are Overrated
Having grown up with the Smalltalk lineage of Object Orientation, the C++/Java world was always alien to me and I spent many hours on fighting with and swearing at the superfluous patterns and frameworks that populated the world of ‘enterprise’ OO programming. The obsession with the holy trinity of encapsulation, inheritance and polymorphism to the cost of messages and interactions did great damage and lead us down the wrong way, or so I believe. And so we ended up with moot discussions about Anemic Domain Models and the evils of getters and setters and the like.
Here’s how I see this.
I consider (public) interfaces and messages primary, along with the actual database schema – thus see the crafting of both of these crucial activities. Needless to say, the in-memory representation of objects will inevitably differ from both the public interface and the database – the question is, how. We know from Domain Driven Design that CRUD-like interfaces are typically warning signs that the business domain and the needs were not clearly understood and are not being served well. Still, REST-ful interfaces are all the rage because HTTP is here to stay, so what to do?
- Create fine-granular, CQRS-friendly endpoints, targeting very specific use cases and not more
- Declare ad-hoc DTOs, serving one request only, even with public attributes if simpler that
- Such transfer objects can be denormalised, aggregates, or otherwise tailored to serve a purpose
- Note that messages often have contextual keys in the URL that don’t need to be in the command
- Use a library that allows such payload’s extraction-from and merging-into domain objects
- Let various components define their own runtime-optimised representation of domain objects
- Keep all business logic in hexagonally or otherwise isolated services, logic or function classes
- Map internal data objects into the various DTOs via utilities in the adapters of the hexagon
- Create the database schema with as much care as the public API, use constraints for integrity
- Map database objects into memory, possibly into several different runtime or transfer objects
The interesting outcome of this approach that we end up with contextual, situationally optimised representations of domain objects, pretty much all without domain logic. One will live in the database schema, another maps into the database, one or maybe more in every component or service, and one transfer object accompanying every message endpoint. In other words, we face the reality that there is no ONE representation of THE domain object and don’t try to shoehorn everything into that constraint, but allow the actual data objects and attributes manifest to serve a need.
And it’s good like that because not objects but interactions matter.