open atlas
↑ Back to track
Architecture Patterns ARCH · 05 · 03

The entity core and the cost

The Entities ring is the most stable code in the system — pure business rules with no dependencies. Clean Architecture's real cost is the mapping and indirection tax it charges on every ring boundary. That cost only pays off in specific circumstances.

ARCH Middle ◷ 24 min
Level
FoundationsJuniorMiddleSenior

Eighteen months after adopting Clean Architecture for the B2B order platform, the team did an audit. The good news: the platform had been extended with four new delivery mechanisms — a mobile app, a partner API, a batch reconciliation service, and an ops CLI — all plugging into the existing interactors with no business logic changes. The new engineers could find every business rule in one place. The bad news: the codebase had ballooned. There were now five separate data structures for an “order” — the Order entity in the Entities ring, the OrderRecord ORM class in the database adapter, the OrderResponseModel in the Use Cases ring, the OrderDTO in the REST adapter, and the OrderGraphQLType in the GraphQL adapter. Every ring boundary produced mapping code. A simple “add a field to an order” change touched all five classes and three mapping functions. The team asked the honest question: was the architecture earning its cost? The answer was: for this platform, yes — but they had seen colleagues apply the same pattern to a twenty-table CRUD app for internal reporting and produce the same amount of mapping code for zero structural benefit. Clean Architecture is not universally correct. It has a specific cost and specific conditions under which that cost is justified.

The Entity core — what lives there and why

The Entities ring is the innermost ring in Clean Architecture. It contains enterprise-wide business rules: logic that would be true and valid in any application built on this business domain, regardless of how the application is delivered or what infrastructure it uses.

In the B2B order platform, the Entity core contains:

  • The Order class with its lifecycle invariants (a pending order must have at least one line item before confirmation; a cancelled order cannot transition to confirmed; total must equal sum of line items)
  • The Invoice class with its structural invariants (line items must sum to total; a paid invoice cannot be modified)
  • The Money value object with currency-safe arithmetic rules
  • The OrderStatus, InvoiceStatus enumerations representing valid states
  • Domain events (OrderConfirmed, InvoiceGenerated) as pure data structures

What is absent from the Entities ring: anything that touches infrastructure, application flow, or framework. No repository calls. No async/await wiring for database access. No @Column or @Entity ORM annotations. No HTTP status codes. No logging framework imports. The entity classes are plain objects that could run in a completely isolated test with no external dependencies.

The stability argument

The case for keeping the Entity core pure is the stability argument: entities are the last code you want to touch when technology changes. Databases get migrated. Web frameworks are upgraded. Mobile clients appear. None of these events should change the rule “an Invoice’s total must equal the sum of its line items.” That rule is permanent as long as the business model holds.

By keeping entities free of infrastructure imports, you guarantee that an ORM upgrade cannot trigger entity changes. A switch from REST to GraphQL cannot require touching Order business logic. A new cloud provider mandate cannot ripple into domain rules. The innermost ring changes only when the business model changes — which is the one change that should rightfully affect it.

This stability also has a testing payoff. Entity unit tests are the cheapest tests in the system: instantiate a domain object, call a method, assert a result. No database, no HTTP server, no mock framework. The entity encapsulates its invariants — testing them requires only the entity class and whatever value types it uses.

Why this works

Why “enterprise-wide” for entities specifically? Martin’s terminology distinguishes between rules that would be valid across many applications in the same enterprise (the Order invariants apply whether you are building the customer-facing portal, the operations console, or the finance reporting tool) versus rules that are specific to one application’s workflow (the specific steps for the “place order through the partner API” use case). Entities belong in the innermost ring because they are the most reusable, most stable, and most change-resistant business knowledge in the system. The outer rings are where application-specific, infrastructure-specific, and technology-specific knowledge lives. The innermost ring is as clean as it gets.

The honest cost — mapping and indirection

The structural benefits of Clean Architecture are real. The cost is equally real, and it must be acknowledged before choosing the pattern.

The mapping tax. Every ring boundary requires translation code. A field on an Order entity does not automatically appear in the OrderResponseModel (Use Cases ring), the OrderDTO (REST adapter), or the OrderRecord ORM class (database adapter). You write explicit mapping code at each boundary. The field that starts on the entity typically appears in four or five separate classes and requires three or four mapping functions.

In the B2B platform audit: adding deliveryDeadline to the Order entity meant:

  1. Adding the field to Order (Entities ring) with its invariant (deadline must be after creation date)
  2. Adding it to GenerateInvoiceRequest and GenerateInvoiceResponse where needed (Use Cases ring)
  3. Adding it to OrderRecord and the ORM mapping function (Infrastructure ring)
  4. Adding it to OrderDTO and the serialization code (REST adapter)
  5. Adding it to OrderGraphQLType and the resolver (GraphQL adapter)

This is the indirection tax. In a layered architecture, you might touch two files. In Clean Architecture, you touch five — in exchange for the guarantee that each ring has no knowledge of the others’ formats.

The ceremony of interface definitions. Every repository, every service, every output port requires an interface definition in the Use Cases ring and a concrete implementation elsewhere. For a system with thirty use cases, this means thirty input port interfaces, thirty interactor classes, potentially thirty output port interfaces, thirty request models, and thirty response models. Before writing a single line of business logic, you are scaffolding a significant amount of structure.

The cognitive load. New engineers joining the team have to understand the ring model before they can locate any piece of logic. The onboarding question “where does the billing hold check live?” has a precise answer (Use Cases ring, inside the interactor) — but only once you understand the architecture. In a layered or transaction-script codebase, the answer is “search for the method name.” The Clean Architecture answer is better, but it requires learning the model.

When Clean Architecture is overkill

Clean Architecture pays off in specific circumstances. Outside those circumstances, it generates ceremony without structural return.

Overkill for: CRUD applications. A system that creates, reads, updates, and deletes records without complex invariants, lifecycle state machines, or multiple delivery mechanisms has no business logic to protect. Entities with no real invariants (just getters and setters over a database row) do not benefit from being in a pure innermost ring. The mapping tax is paid with no benefit, because the “protection” the architecture provides (infrastructure changes don’t affect business rules) is meaningless when the business logic is trivially thin.

Overkill for: small or short-lived applications. An internal reporting dashboard used by ten people, a data migration script, a throwaway proof-of-concept — these will be replaced before the architecture’s long-term benefits accrue. The mapping overhead is a concrete upfront cost; the payoff (surviving technology changes over years) never materializes.

Overkill for: teams without the discipline to maintain the boundaries. Clean Architecture’s benefits evaporate the moment a developer adds a framework import to an entity for convenience. The mapping tax is permanent; the structural guarantees are only permanent if the dependency rule is enforced. If the team cannot or will not enforce ring boundaries (through code review, linting, architectural tests), the cost is paid but the benefit is not received.

Worth it for: long-lived applications with complex domain rules. When the business logic is genuinely complex — multiple lifecycle states, invariants that require enforcement, rules that grow over time — the entity core provides the stable foundation that survives years of infrastructure change.

Worth it for: multiple delivery mechanisms over a shared domain. The B2B platform’s four delivery mechanisms (web, mobile, partner API, CLI) all reused the same interactors. Adding the fourth mechanism cost the team only an adapter. Without Clean Architecture, they would have had four copies of the invoice generation logic — four places where the billing hold check could go missing.

Worth it for: teams that will grow and change. When the team that builds the system is not the team that maintains it, explicit contracts (request models, port interfaces) serve as documentation that the compiler enforces. The new engineer who joins in year three can find every rule about order confirmation in one interactor, not scattered across six callsites.

lesson.inset.note

A practical heuristic: count the number of delivery mechanisms that share the same business logic. If the answer is one (just an HTTP API, and it will always be just an HTTP API), Clean Architecture’s symmetry benefit is hypothetical. If the answer is three or more — or if there is a credible roadmap for three or more — the interactor pattern starts to pay. A second heuristic: count the domain invariants. If you can describe all the business rules of your domain in five sentences, the entity core has little to protect. If describing the rules takes a whiteboard session, the core is worth the tax.

Quiz

The platform team adds a new field, `billingRegion`, to the Order entity. In a fully implemented Clean Architecture codebase, how many files must change minimally, and why?

Quiz

A startup is building a B2B order management system as a monolith for a single customer, expected to run for eighteen months before being rebuilt. The CTO proposes Clean Architecture. What is the honest architectural advice?

Quiz

A team is building a B2B order platform that currently has one HTTP API. The product roadmap shows a mobile app, a partner EDI integration, and an ops CLI planned for the next two years. The CTO asks whether Clean Architecture is worth adopting now or whether they should 'wait and see.' What is the structural argument for adopting it at the start?

Recall before you leave
  1. 01
    What belongs in the Entity core, and what is the stability argument for keeping it pure?
  2. 02
    What is the mapping tax, and what makes it worth paying?
  3. 03
    Name three signs that Clean Architecture is overkill for a given project.
Recap

The Entity core is the innermost ring in Clean Architecture — the most stable, most abstract, most independently testable code in the system. It contains enterprise-wide business rules: the invariants that would be true in any application built on this business domain, regardless of delivery mechanism or infrastructure technology. What is absent: ORM annotations, database types, HTTP concepts, framework imports. The entity changes only when the business model changes.

The honest cost of Clean Architecture is the mapping tax. Every ring boundary charges it: a domain field appears in multiple data structures, each ring’s format suited to its purpose, each boundary requiring explicit mapping code. This is not a quirk of bad implementation — it is the direct structural consequence of ring separation. The platform audit found five separate classes for “order” and three mapping functions per field. That is the bill.

The bill is worth paying under specific conditions: long-lived applications where infrastructure changes outpace business-model changes; systems with multiple delivery mechanisms that reuse the same business logic; complex domains where the entity core genuinely has invariants to protect; teams that will grow and change, where explicit contracts outlast any one engineer’s knowledge. For CRUD applications, throwaway systems, single-delivery-mechanism backends, and domains with trivial invariants, the bill arrives without a corresponding payoff.

This completes the unit on Clean and Onion Architecture. The next unit introduces Domain-Driven Design — which takes the entity core concept further and asks how to model the business domain so that the model itself becomes the architecture.

Practice

Start at the top. Tasks go easiest → hardest: recall a fact, apply it to a case, then a senior-level stretch. Open one, attempt it, then reveal.

recallapplystretch0 of 4 done

Something unclear?

Ask a question about this lesson. Questions are anonymous and go straight to the author to make the lesson better.

shortcuts expand
search
K
prev piece
k
next piece
j
cycle tier
t
this menu
?
sources3
expand
  1. 01
  2. 02
  3. 03

Trademarks belong to their respective owners. Editorial reference only.