10 Data Oriented Architecture
In this chapter, the core idea is that software should be designed around data first, with objects used mainly as boundary-crossing tools that move data between parts of the system. The chapter begins with a simple ingestion scenario where external files are read, transformed, and written to a database, and shows how an object-oriented approach naturally tends to mirror the shape of external dependencies. That approach can feel elegant, but it often leaks outside details into the core program, making tests cumbersome, changes difficult, and the system tightly coupled to libraries and protocols that should have remained at the edges.
The data-oriented approach reverses that pressure by asking what data the program actually needs, what shape that data should take, and how it should move through the system. Rather than starting with abstractions over behavior, it starts with well-modeled data types and a clear flow from raw input to domain data to database-ready commands. This leads to a different way of thinking about architecture: separate how you get data from what you do with it, defer abstraction until the data shape is clear, and let the program speak in terms of its own semantics instead of the semantics imposed by external systems.
The chapter frames architecture as three interacting worlds: what uses the program, what the program uses, and the core program itself. Good boundaries make the middle world resilient by forcing the outside worlds to adapt to the program’s data model and intent, not the other way around. Objects still matter, but their role changes: they become gateways that vend the exact data shape the application demands, while deterministic logic stays isolated and easy to test. When a dependency cannot match the program’s needs, the solution is to create a boundary object in the right place so that change stays contained, semantics remain clear, and future modifications become local rather than contagious.
The basic setup we’ll use to explore code architecture. It’s ambiguous by design!
Identifying common behaviors
Poor abstractions leak the knowledge they should hide
Change flows inward forcing everything to update
The common N-Tier architecture
Software architecture as three distinct worlds
Categorizing the dependencies we use
Three worlds from the perspective of a REST API
What our program uses versus what uses our program
These worlds are antagonistic to each other
Good design forces the other worlds to comply to the way we want things to be
The same mental model can be applied from multiple perspectives
Give yourself room to design
Categorizing which data belongs to our program
“Give me data in this shape”
A mismatch between what we’re demanding and what existing objects offer
Corruption of our beautiful world
Forcing the CloudStore to fit our design
The outside world’s unyielding attempts at corrupting our design
Good boundaries prevent change from flowing into your core program
Our core program no longer knows that CSV exists
The core handles what’s deterministic, the shell handles what’s not
The Arrows are going the wrong direction
The external world wants you to do things its way
Demand that the outside worlds comply to the semantics you desire
The demand for clear semantics drives good boundaries
The slow steady corruption of missing APIs.
The push / pull of design.
The remaining boundary to draw
Demanding the Manifest information as well typed data
Good data types conjure well scoped objects into existence
Leaking database details into the program’s core
changes to our service integration
No sweeping refactors needed. Just hook up a new front door.
Summary
- Software architecture, to quote Erlang’s creator, is “a way of viewing the world.”
- Separate how you get data from what you do with it.
- Your program is not a REST API, GraphQL API, or [any other technology here]
- Your program is not the database it uses or the services it calls
- Your program has a central core that exists independently of everything else
- Be cautious of abstraction and focusing on what things do!
- Focus on the data and how it moves through your system
- Design the world as you want it to be. Don’t accept it as given.
- Organize object ownership around data, not functionality
- Resist the urge to abstract!
- Abstracting too early can fool us into missing the most important boundaries
- When designing, start in the “middle” and work your way out.
- We push our design onto the outside worlds, not the other way around
- Force dependencies to use your semantic data types
- Beware the subtle, tireless corruption of the outside world
- “What’s already there” is a helluva drug. It will influence your thinking without caution.
- In DoP, the primary role of objects is to vend data to our core program
- OOP offers functionality; DoP demands data
- Let simple boring things be simple and boring. Not everything needs to be extensible
- Look around your codebase. Are those “abstractions” paying off?
- You Ain’t Gunna Need It makes fools of us all.
- Change is inevitable. Sometimes the best defense is no defense. Let it change.
- Dependencies will slowly corrupt your codebase by offering APIs that don’t quite fit
- Create wrappers consciously! Don’t just repackage the underlying dependencies API.
- Good wrappers create new semantic layers in the application
- Our program will demand compliance, but give each object the right to say “no.”
- Good boundaries make change easy.
Data-Oriented Programming in Java ebook for free