Overview

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.

FAQ

What is the main idea of data-oriented architecture in this chapter?It is a way of designing software around data first, then using objects only as boundaries or gatekeepers to move that data between worlds. The goal is to keep the core program focused on the shapes and semantics of data, not on external implementation details.
Why does the chapter say objects still matter in data-oriented architecture?Objects still matter because they are used to satisfy data demands at the boundaries of the system. In this architecture, objects are not mainly about behavior; they are used to vend or adapt data in the shape the core program wants.
What are the “three worlds” described in the chapter?The three worlds are the core program, the things it uses, and the things that use it. The core is where design happens, the outside worlds include dependencies like databases and cloud storage, and the boundary between them is what architecture must protect.
Why does the chapter recommend starting architecture in the middle?Starting in the middle helps prevent outside dependencies from corrupting the design too early. By first modeling the data the program actually needs, you can define boundaries that force external systems to adapt to your semantics instead of the other way around.
What is wrong with abstracting too early in this example?Early abstraction tends to mirror the structure of external systems rather than the needs of the core program. It can leak details, make testing harder, and create dependencies where the application must know too much about files, databases, or libraries.
How does the chapter contrast object-oriented thinking with data-oriented thinking?OO thinking usually starts by asking what objects do and fitting code to interfaces and strategies. Data-oriented thinking starts by asking what the data is and how it should move through the system, then creates objects only when needed to support that flow.
Why is “separate how you get data from what you do with it” so important?This separation keeps non-deterministic, messy operations like I/O outside the core logic. It makes the core easier to reason about, easier to test, and less vulnerable to changes in external systems.
What role does a FileRepository play in the architecture?The FileRepository is a boundary object that adapts the outside world’s file-based API into the domain data types the program wants. It hides details like cloud storage, input streams, and CSV parsing from the core program.
Why does the chapter discourage wrapper classes that simply repackage another API?Because wrappers that merely mirror an external API still force the core program to think in the same terms as the dependency. Good boundary objects should translate external semantics into the program’s own semantics, not just rename the same concepts.
How does good boundary design make future change easier?When boundaries are well placed, changes in external systems usually require updating only one object at the edge instead of many places in the core. That means new integrations, asynchronous processing, or library swaps can often be handled without major refactoring.

pro $24.99 per month

  • access to all Manning books, MEAPs, liveVideos, liveProjects, and audiobooks!
  • choose one free eBook per month to keep
  • exclusive 50% discount on all purchases
  • renews monthly, pause or cancel renewal anytime

lite $19.99 per month

  • access to all Manning books, including MEAPs!

team

5, 10 or 20 seats+ for your team - learn more


choose your plan

team

monthly
annual
$49.99
$499.99
only $41.67 per month
  • five seats for your team
  • access to all Manning books, MEAPs, liveVideos, liveProjects, and audiobooks!
  • choose another free product every time you renew
  • choose twelve free products per year
  • exclusive 50% discount on all purchases
  • renews monthly, pause or cancel renewal anytime
  • renews annually, pause or cancel renewal anytime
  • Data-Oriented Programming in Java ebook for free
choose your plan

team

monthly
annual
$49.99
$499.99
only $41.67 per month
  • five seats for your team
  • access to all Manning books, MEAPs, liveVideos, liveProjects, and audiobooks!
  • choose another free product every time you renew
  • choose twelve free products per year
  • exclusive 50% discount on all purchases
  • renews monthly, pause or cancel renewal anytime
  • renews annually, pause or cancel renewal anytime
  • Data-Oriented Programming in Java ebook for free