Data Sinks and Logic Floats

In effective layered architectures, data sinks to the bottom, and logic floats to the top. The more narrowly-scoped the logic, the higher it floats. The more permanent the data, the deeper it sinks.

Photo of mountains showing rock strata.
Layered rocks, because you can't really get a good photo of the layers of the ocean. Photo by Joseph Corl / Unsplash.

When we say "deeper in the stack" we usually mean a dependency, or a dependency's dependency, or... I don't hear "shallower" very often—sometimes "higher"—but the idea is there; it's the other direction. Together, they let us talk about decision-making criteria for where something new should go in the stack.

In effective layered architectures, data sinks to the bottom, and logic floats to the top. The more narrowly-scoped the logic, the higher it floats. The more permanent the data, the deeper it sinks.

The lightest parts a web product stack have the most logic and most transient data: user interfaces. UIs are full of context-specific presentation logic, logic that would be meaningless to deeper layers. Opening a menu, loading page two of a list, showing a warning before a destructive action, all of the little "swoosh" moments—a quarter-second transition here, a subtle shadow there—that make a product feel polished and nice to use. That logic may not even be meaningful to another UI: Android and iOS apps had different navigation conventions for a long time. And it may be pretty ephemeral: Androids used to have back buttons. Customer-facing APIs also belong here, since APIs are UIs in technical products.

a person swimming in the ocean with a mountain in the background
Just beneath the surface. Like right there. Photo by NEOM / Unsplash

Below the UIs are the things that only exist to power the UIs. A backend-for-frontend (BFF) is a backend designed to support a small number of UIs. Systems at this depth implement logic like authentication (AuthN), sometimes authorization (AuthZ), scatter-gather fetching, reformatting and combining responses—logic that the UI needs but isn't meaningful to deeper layers. A GraphQL server is a perfect example.

Below the BFFs and API layers, strange, unique systems lurk in the murky waters. This is where you find "business logic." Decisions like "do we send this notification now?" or "which of these two flights is less painful?" are usually made here. This is where most of the unique technology value in most companies sits. A ProductMatcher system that takes what it knows about a customer to recommend the best-fitting products would live here. You probably won't release these services as open source.

Photo of an active deep sea vent with plumes of black smoke against a nearly black background.
Dark smoke in inky blackness? Still less scary than the fish that live down here. WikiMedia / MARUM.

In the deepest reaches of the stack you find the most critical systems: data stores for business primitives—the most basic, core entities in your data model. Users, flights, authors, products, inventory, articles, pictures. Because of the calculus of availability—everything above relies on them—these systems need to be the most reliable and so tend to be simple. They usually perform CRUD operations and sometimes AuthZ.

Logic floats upward, and the more unique or specific the logic, the more buoyant it is—and the more ephemeral. UI patterns, design systems, policies, brand guidelines change year to year as users expectations evolve. The resources we need to gather and the format or protocol we need them in change to power those UIs. How to build an email or use the Slack API changes a lot more slowly.

Data is heavy. Data has mass—and so it has inertia; issues with data take the longest to fix and are some of the most contagious types of technical debt. Changing a data model, especially a conceptual data model, is expensive. I like the term "business primitive" because these entities tend to represent the thing that the business does. There's no fallback for these. For Condé Nast: articles, images, authors. For Policygenius: customers, policies. For Fastly: services, domains. Systems that persist these things need to be rock solid and sit, like rocks, on the substrate. And the more permanent the data, the heavier it gets: sessions, derived or cached states, the state of an in-progress process are all short-lived and will float toward the surface, while the primitives sink down, and intermediates like sub-system audit logs might stay in the middle.

Having this map gives you both a vocabulary and some rules of thumb for questions like where new pieces fit in and which systems can depend on each other.