Remote Facade and DTO
Purpose
Make a remote interface coarse enough to be usable over a network, and make the data that
crosses it a deliberate contract rather than an accidental serialisation. These two patterns
travel together: a coarse operation needs a payload that carries everything the caller needs
in one exchange.
Two failures bracket the topic. The chatty facade: a remote API that mirrors the domain
model, so rendering one screen costs five round trips and the interface's latency is
dominated by the network. The ceremonial DTO: a field-for-field copy of an entity, with a
mapper, a test and a maintenance burden, that provides no decoupling because it changes
whenever the entity does.
The patterns
Remote Facade a coarse-grained object over a fine-grained model,
offering complete business operations. It holds no
business logic — it translates one remote request into
calls on the local model and assembles the answer.
DTO a simple carrier of data across the boundary, shaped by
what the caller needs, encodable by the chosen wire format, with no domain policy
and no dependency on the domain's internals.
Compatibility and evidence
Inspect the target compiler/runtime, serializer and framework versions, existing payloads
and consumer contracts before changing types. Records require Java 16+ and serializer
support; the Spring ProblemDetail snippets require Spring Framework 6+ (Java 17+).
Examples are partial sketches, with application types, wiring and authorization omitted;
they do not authorize upgrades or new dependencies. A DTO need not implement Java
Serializable to be encoded as JSON or another wire format.
When caller traces, payloads or compatibility tests are unavailable, state the gap and keep
coarsening/removal recommendations conditional. A local facade can simplify an interface
without remote serialization or speculative distribution.
Workflow
- Start from the caller's use case, not from the domain model. What does the caller do
in one interaction? That is one facade operation.
- Count and budget round trips for each interaction. More than one is not automatically wrong:
cacheability, parallelism, reuse, payload size and consistency determine whether coarsening wins.
- Shape the payload from what the caller needs — not the entity's fields, and not
everything that might be useful.
- Decide what the boundary owes: stable field names, documented codes, a version
policy, and explicit nullability. That is the contract
(
rpc-and-api-contracts).
- Materialize required persistent state within its valid context, with a transaction
and isolation level when consistency requires them. Pure mapping of materialized values
may occur afterwards; nothing lazy or managed should escape
(
orm-behavioral-patterns).
- Justify each DTO. If it is an exact copy of a domain type and there is no independent
evolution, no security filtering and no serialisation concern, it may not be earning its
keep — see the decision rules.
Decision rules
The boundary is remote (HTTP, gRPC, messaging)
→ an explicit wire schema/type. A dedicated DTO is usual; a stable
immutable boundary value or generated message may already be it.
The boundary is a public or partner API
→ explicit wire contract (DTO, generated binding or deliberate boundary
value), plus explicit versioning and documented codes.
The domain must be free to change without breaking clients.
The type is a JPA entity
→ DTO, always. Serialising an entity couples the contract to the
schema and drags lazy proxies into the serialiser.
The domain type is already an immutable value with no persistence
concerns and no hidden fields (a record: Money, DateRange, an event)
→ it may cross directly if all components and encoding satisfy the
public contract. A separate type can still provide independent evolution;
check names, nulls, number/time formats and sensitive fields.
Internal, in-process, same deployable, same team
→ usually no DTO. Passing the domain type is simpler, and the
"boundary" can be changed in one commit if it moves.
The caller needs 3 fields of a 40-field aggregate
→ a projection, not a DTO built from the loaded aggregate. Do
not load what you will not send
(query-objects-and-specifications).
Several services need "the same" DTO
→ prefer independently owned representations or versioned schema bindings.
A shared data-only artifact may work with independent version pinning;
avoid forced upgrades and shared domain behavior
(distribution-boundaries).
One client needs a screen-shaped payload and others do not
→ consider a client-specific representation or BFF when separate
ownership/evolution pays for its operational cost.
Rules
- A remote interface normally needs operations coarse enough for its latency and failure budget.
Fine-grained operations can be legitimate for streaming, independently cacheable resources or
genuinely independent workflows. Ported call for
call, a local design becomes a chatty remote one, and no serialiser or protocol makes up
for the round trips (
architecture-and-performance).
- A Remote Facade holds no business logic. It translates, assembles and delegates. Rules
in the facade cannot be reached by any other caller — a job, a consumer, another API — and
they will be duplicated there (
service-layer-design).
- The facade is also the natural place for boundary-only concerns: coarse authorisation for
the operation, request validation, translation of domain failures into the protocol's
error shape, and idempotency-key handling (
idempotency).
- Never serialise a JPA entity to a client. Three couplings arrive at once — schema to
contract, lazy proxies to the serialiser, and internal fields to the public payload — and
each fails differently.
- DTOs are not free and not mandatory. The mapping is code to write, test and keep in
step. Their justification is independent evolution, deliberate exposure, and a stable wire
shape; where none of those applies, the mapping is ceremony
(
enterprise-architecture-smells).
- Prefer immutable DTOs when the serializer supports them. Records are only shallowly
immutable: defensively copy mutable components and ensure nested values are safe to share.
A serializer requiring mutable beans needs controlled construction/publication instead.
- Separate writable request fields from readable response fields. Bind only explicitly
allowed input fields; derive tenant/owner/security scope from trusted context and authorize
each referenced object. Never let generic mapping populate privilege, balance or version
fields merely because their names match. Response/error fields need exposure review too.
- Be explicit about what is absent. A field omitted, a field null, and a field with an
empty value mean different things to a client; decide which you use and be consistent.
- Avoid letting one screen's evolution accidentally control every consumer's contract.
A separate representation in the existing API or a BFF can isolate that change; choose
according to ownership, reuse and operational cost (
view-and-representation-patterns).
- Prefer sharing a language-neutral schema and generating versioned types. A shared DTO artifact can
be acceptable within one release train or as generated data-only bindings when consumers may pin
old versions; hand-written behavioral types and forced upgrades create lockstep coupling
(
distribution-boundaries).
- Additive change is often compatible for tolerant readers, but required fields, closed schemas,
enums and generated clients can break on additions. Removal/renaming are generally breaking. Design the contract so
clients tolerate unknown fields, and expand before you contract
(
rpc-and-api-contracts).
- Prefer bounded scalar projections when they satisfy the read contract. Materialize lazy
state before leaving its valid persistence context; mapping detached, already materialized
values is safe. A coarse endpoint spanning services does not create a distributed
transaction or a consistent snapshot.
- The mapper is not a place for business rules. A mapper that computes a total or decides a
status has hidden a rule where no test looks for it.
Deliverable
Provide the operation/payload change, preserved authorization and wire semantics, measured
round-trip/payload trade-off or missing evidence, and focused checks for old clients, invalid
input, sensitive fields and partial/repeated execution. Keep small reviews short.
References
- Remote Facade — coarsening an interface with the round-trip
arithmetic, what belongs in a facade and what must not, batch and partial-failure
operations, idempotency and conditional requests at the boundary, and the facade as the
place where domain failures become protocol errors. Read when designing a remote API or
diagnosing a chatty one.
- DTO versus domain object — the decision table with
the cases where a DTO is mandatory, optional and wasteful; mapping strategies and their
failure modes; projections instead of DTOs over loaded aggregates; the shared-DTO-library
trap; and how to shrink an over-mapped codebase safely. Read when a DTO layer is being
added, questioned, or has become a burden.
1---2name: remote-facade-and-dto3description: Designing what crosses a remote boundary: a Remote Facade providing coarse, business-shaped operations, and DTOs carrying the data in one round trip — plus when a DTO earns its mapping cost. Use when an API mirrors the domain model method for method, when a client makes five calls to render one screen, when JPA entities are serialised to clients, when a DTO is a field-for-field copy of an entity, when adding a field means editing seven classes, when internal fields appear in a public payload, or when a shared DTO library couples services at compile time. Does not cover whether the boundary should be remote (distribution-boundaries), contract versioning (rpc-and-api-contracts), the view layer (view-and-representation-patterns), or the application service the facade calls (service-layer-design).4---56# Remote Facade and DTO78## Purpose910Make a remote interface coarse enough to be usable over a network, and make the data that11crosses it a deliberate contract rather than an accidental serialisation. These two patterns12travel together: a coarse operation needs a payload that carries everything the caller needs13in one exchange.1415Two failures bracket the topic. The **chatty facade**: a remote API that mirrors the domain16model, so rendering one screen costs five round trips and the interface's latency is17dominated by the network. The **ceremonial DTO**: a field-for-field copy of an entity, with a18mapper, a test and a maintenance burden, that provides no decoupling because it changes19whenever the entity does.2021## The patterns2223```text24Remote Facade a coarse-grained object over a fine-grained model,25 offering complete business operations. It holds no26 business logic — it translates one remote request into27 calls on the local model and assembles the answer.2829DTO a simple carrier of data across the boundary, shaped by30 what the caller needs, encodable by the chosen wire format, with no domain policy31 and no dependency on the domain's internals.32```3334## Compatibility and evidence3536Inspect the target compiler/runtime, serializer and framework versions, existing payloads37and consumer contracts before changing types. Records require Java 16+ and serializer38support; the Spring `ProblemDetail` snippets require Spring Framework 6+ (Java 17+).39Examples are partial sketches, with application types, wiring and authorization omitted;40they do not authorize upgrades or new dependencies. A DTO need not implement Java41`Serializable` to be encoded as JSON or another wire format.4243When caller traces, payloads or compatibility tests are unavailable, state the gap and keep44coarsening/removal recommendations conditional. A local facade can simplify an interface45without remote serialization or speculative distribution.4647## Workflow48491. **Start from the caller's use case**, not from the domain model. What does the caller do50 in one interaction? That is one facade operation.512. **Count and budget round trips** for each interaction. More than one is not automatically wrong:52 cacheability, parallelism, reuse, payload size and consistency determine whether coarsening wins.533. **Shape the payload from what the caller needs** — not the entity's fields, and not54 everything that might be useful.554. **Decide what the boundary owes**: stable field names, documented codes, a version56 policy, and explicit nullability. That is the contract57 (`rpc-and-api-contracts`).585. **Materialize required persistent state within its valid context**, with a transaction59 and isolation level when consistency requires them. Pure mapping of materialized values60 may occur afterwards; nothing lazy or managed should escape61 (`orm-behavioral-patterns`).626. **Justify each DTO.** If it is an exact copy of a domain type and there is no independent63 evolution, no security filtering and no serialisation concern, it may not be earning its64 keep — see the decision rules.6566## Decision rules6768```text69The boundary is remote (HTTP, gRPC, messaging)70 → an explicit wire schema/type. A dedicated DTO is usual; a stable71 immutable boundary value or generated message may already be it.7273The boundary is a public or partner API74 → explicit wire contract (DTO, generated binding or deliberate boundary75 value), plus explicit versioning and documented codes.76 The domain must be free to change without breaking clients.7778The type is a JPA entity79 → DTO, always. Serialising an entity couples the contract to the80 schema and drags lazy proxies into the serialiser.8182The domain type is already an immutable value with no persistence83concerns and no hidden fields (a record: Money, DateRange, an event)84 → it may cross directly if all components and encoding satisfy the85 public contract. A separate type can still provide independent evolution;86 check names, nulls, number/time formats and sensitive fields.8788Internal, in-process, same deployable, same team89 → usually no DTO. Passing the domain type is simpler, and the90 "boundary" can be changed in one commit if it moves.9192The caller needs 3 fields of a 40-field aggregate93 → a projection, not a DTO built from the loaded aggregate. Do94 not load what you will not send95 (query-objects-and-specifications).9697Several services need "the same" DTO98 → prefer independently owned representations or versioned schema bindings.99 A shared data-only artifact may work with independent version pinning;100 avoid forced upgrades and shared domain behavior101 (distribution-boundaries).102103One client needs a screen-shaped payload and others do not104 → consider a client-specific representation or BFF when separate105 ownership/evolution pays for its operational cost.106```107108## Rules109110- A remote interface normally needs operations coarse enough for its latency and failure budget.111 Fine-grained operations can be legitimate for streaming, independently cacheable resources or112 genuinely independent workflows. Ported call for113 call, a local design becomes a chatty remote one, and no serialiser or protocol makes up114 for the round trips (`architecture-and-performance`).115- **A Remote Facade holds no business logic.** It translates, assembles and delegates. Rules116 in the facade cannot be reached by any other caller — a job, a consumer, another API — and117 they will be duplicated there (`service-layer-design`).118- The facade is also the natural place for boundary-only concerns: coarse authorisation for119 the operation, request validation, translation of domain failures into the protocol's120 error shape, and idempotency-key handling (`idempotency`).121- **Never serialise a JPA entity to a client.** Three couplings arrive at once — schema to122 contract, lazy proxies to the serialiser, and internal fields to the public payload — and123 each fails differently.124- **DTOs are not free and not mandatory.** The mapping is code to write, test and keep in125 step. Their justification is independent evolution, deliberate exposure, and a stable wire126 shape; where none of those applies, the mapping is ceremony127 (`enterprise-architecture-smells`).128- Prefer immutable DTOs when the serializer supports them. Records are only shallowly129 immutable: defensively copy mutable components and ensure nested values are safe to share.130 A serializer requiring mutable beans needs controlled construction/publication instead.131- Separate writable request fields from readable response fields. Bind only explicitly132 allowed input fields; derive tenant/owner/security scope from trusted context and authorize133 each referenced object. Never let generic mapping populate privilege, balance or version134 fields merely because their names match. Response/error fields need exposure review too.135- **Be explicit about what is absent.** A field omitted, a field null, and a field with an136 empty value mean different things to a client; decide which you use and be consistent.137- Avoid letting one screen's evolution accidentally control every consumer's contract.138 A separate representation in the existing API or a BFF can isolate that change; choose139 according to ownership, reuse and operational cost (`view-and-representation-patterns`).140- Prefer sharing a language-neutral schema and generating versioned types. A shared DTO artifact can141 be acceptable within one release train or as generated data-only bindings when consumers may pin142 old versions; hand-written behavioral types and forced upgrades create lockstep coupling143 (`distribution-boundaries`).144- Additive change is often compatible for tolerant readers, but required fields, closed schemas,145 enums and generated clients can break on additions. Removal/renaming are generally breaking. Design the contract so146 clients tolerate unknown fields, and expand before you contract147 (`rpc-and-api-contracts`).148- Prefer bounded scalar projections when they satisfy the read contract. Materialize lazy149 state before leaving its valid persistence context; mapping detached, already materialized150 values is safe. A coarse endpoint spanning services does not create a distributed151 transaction or a consistent snapshot.152- The mapper is not a place for business rules. A mapper that computes a total or decides a153 status has hidden a rule where no test looks for it.154155## Deliverable156157Provide the operation/payload change, preserved authorization and wire semantics, measured158round-trip/payload trade-off or missing evidence, and focused checks for old clients, invalid159input, sensitive fields and partial/repeated execution. Keep small reviews short.160161## References162163- [Remote Facade](references/remote-facade.md) — coarsening an interface with the round-trip164 arithmetic, what belongs in a facade and what must not, batch and partial-failure165 operations, idempotency and conditional requests at the boundary, and the facade as the166 place where domain failures become protocol errors. Read when designing a remote API or167 diagnosing a chatty one.168- [DTO versus domain object](references/dto-vs-domain-object.md) — the decision table with169 the cases where a DTO is mandatory, optional and wasteful; mapping strategies and their170 failure modes; projections instead of DTOs over loaded aggregates; the shared-DTO-library171 trap; and how to shrink an over-mapped codebase safely. Read when a DTO layer is being172 added, questioned, or has become a burden.