Clean Architecture's Interface Adapters ring contains Controllers and Presenters that sit between Use Cases and the outside world. Concretely, what data crosses a use-case boundary in each direction, and why shouldn't a use case just accept and return its own Entity objects directly to a REST controller?
answer
- input DTO in, output DTO out
- controller builds input, presenter builds view model
- output boundary = callback interface
- entities never cross out raw
- wire shape != domain shape
basics
~20 sA controller converts an incoming request into a simple input object the use case understands; the use case returns a simple output object; a presenter turns that into a view model/JSON. Passing entities straight out would leak internal business objects (and their behavior) into the web layer.
solid answer
~50 sEach use case defines its own input and output boundary types - plain data structures like PlaceOrderInputData/PlaceOrderOutputData - independent of both the HTTP layer and the entity's internal shape. A Controller takes the framework-specific request object and maps it into the input data type before calling the use case; on the way out, the use case emits an output data type (often via an output boundary interface/callback so the use case doesn't even know a 'response' exists) and a Presenter maps that into a view model or JSON payload the delivery mechanism understands. Returning raw Entities to a controller instead couples the wire format and the domain model together, so a change made purely for API compatibility (renaming a field for a client) forces a change to core business objects, and it risks exposing entity methods/state (e.g., internal calculation methods, unvalidated fields) directly to the transport layer.
go deeper
Can state that data gets converted at the boundary rather than passed through unchanged.
Can trace a concrete example of a field from HTTP request through input DTO, use case, output DTO, to JSON response.
Can explain the risk of serializing entities directly (leakage, lazy-loading exceptions) with a concrete production failure scenario.
Can judge when to skip the DTO/presenter ceremony for a specific low-risk service and articulate exactly what risk is being accepted by doing so.
## What crosses in each direction Every use case in Clean Architecture defines its own input and output boundary types - plain data-holding objects with no behavior and no dependency on any framework, sometimes literally suffixed `InputData/OutputData` or `Request/Response` in Uncle Bob's own examples. - **Going in**, a Controller (living in the Interface Adapters ring) receives a framework-specific artifact - an HTTP request object, a parsed CLI argument list, a deserialized gRPC message - and is responsible for translating it into that input data type before invoking the use case's `execute()` method; the use case itself never sees the HTTP request. - **Going out**, the use case doesn't return a value directly in the canonical formulation - it calls an output boundary interface (essentially a callback, often literally named something like `PlaceOrderOutputBoundary`) with an output data object, and a Presenter implementing that interface converts the output data into a view model shaped for the specific delivery channel - JSON for a REST client, a formatted string for a CLI, a template-ready object for server-rendered HTML. ## The temptation to hand entities back The temptation to skip this and have the use case accept/return Entity objects directly, letting the controller serialize the entity straight to JSON, is strong because it's less code - but it collapses two things that need to vary independently: the shape of your core business object, and the shape of your wire format. An `Order` entity might carry internal-only fields (an audit trail, a pricing calculation cache, a reference to an internal `RiskFlag` object) that should never reach a client; if the controller just serializes the entity, a client library upgrade or an ORM lazy-loading change can silently leak that internal state, or trigger a runtime error trying to serialize a lazy-loaded proxy that has no active database session outside the transaction. DTOs/view models exist specifically to be the thing that's allowed to change for API-consumer reasons (renaming a field for a mobile client, adding a computed `displayPrice`) without forcing a change to the domain object that encodes actual business rules. ## Why the output boundary is an interface The output-boundary-as-interface pattern (rather than a plain return value) is the more subtle half of this and is often missed: it exists so that even the direction of control stays inverted. If `execute()` simply returned an output object, the use case would still be fine, but many strict readings of Clean Architecture prefer the callback style because it lets the use case be entirely blind to whether it's being called synchronously (web request/response) or asynchronously (a message consumer that has no 'return' concept at all, only a place to publish a result). The use case just calls 'here is your output' on an interface; whether that turns into an HTTP 200 or a Kafka message is entirely the presenter's business. ## What the machinery costs This machinery costs real files and real mapping code - for one use case you may end up with several conversions for what might be a single field passing through unchanged: - an input DTO; - an output DTO; - a mapper turning entity fields into the output DTO; - a presenter turning the output DTO into a view model; - a serializer turning the view model into JSON. For an internal admin tool with one trusted client and no meaningful difference between 'domain shape' and 'wire shape,' this is legitimate overkill, and teams reasonably let the entity (or a thin wrapper) serialize directly, accepting the coupling because the two shapes are never expected to diverge. ## The failure mode on each side - **Entity leakage.** The failure mode on one side is exactly what this pattern guards against - accidental entity leakage causing either data exposure or brittle serialization bugs. - **Over-mapping.** The failure mode on the other side is over-mapping: a presenter or DTO layer that is a 1:1 field-for-field copy of the entity with no actual transformation, maintained purely out of ritual; when a field is added to the entity, a developer has to remember to also add it to the DTO and the mapper, and forgetting is a common, easy-to-miss bug (a new required field silently missing from every API response) that a straight pass-through would never have had. ## A worked example In a subscription-billing use case, `CancelSubscriptionUseCase.execute(CancelSubscriptionInput)` loads the `Subscription` entity (which has internal fields like `gracePeriodPolicy` and `internalNotes` used only for dunning logic), applies the cancellation rule, and calls `outputBoundary.present(CancelSubscriptionOutput(subscriptionId, effectiveDate, refundAmount))`. The `CancelSubscriptionPresenter` implementing that boundary builds a `CancellationResponseJson(subscriptionId, effectiveDate, refundAmount, "Your subscription ends on ...")` with a client-facing human-readable message that has no equivalent field on the entity at all - a transformation only possible because the boundary types are separate from the entity in the first place.
- Why use a callback-style output boundary interface instead of just having execute() return the output object directly?It keeps the use case agnostic about whether the caller is synchronous (an HTTP request awaiting a response) or asynchronous (a message consumer with no 'return' concept), since the use case just calls 'here's your result' on an interface, and whatever implements it - a presenter, a message publisher - decides what to do with that.
- What's a realistic cost of maintaining separate input/output DTOs alongside the entity for every use case?Field duplication and mapping code: when a field is added to the entity, a developer has to remember to also update the relevant DTO(s) and the mapper between them, and forgetting is a subtle bug (a field silently missing from an API response) rather than a build failure.
- For a small internal admin tool with one trusted client, is it ever reasonable to skip the DTO/presenter layer and serialize an entity directly?Yes - when the domain shape and the wire shape are never expected to diverge and there's no risk of exposing sensitive internal fields to an untrusted client, the mapping ceremony is legitimate overhead to cut, accepting the coupling as a deliberate, low-risk trade-off rather than an oversight.
Like a diplomatic interpreter at a summit: the head of state (the use case) states their actual position in their own language and terms; the interpreter (controller/presenter) converts that into whatever language and register the other party expects, without the head of state having to learn that language or having their own words permanently altered by the translation.
saying these in an interview costs you the question
- has a controller serialize the entity directly to the HTTP response without a DTO/view-model step
- can't explain why the output crosses via an interface/callback rather than a plain return in the canonical formulation
- doesn't recognize lazy-loaded ORM associations as a concrete risk of serializing entities directly
- treats input and output boundary types as literally the same object reused in both directions