MVC and Request Handling
Purpose
Keep the web layer to its actual job — turning a request into a call and a result into a
response — and put shared request concerns in one place instead of in every handler. Modern
frameworks implement these patterns for you, so the value here is not in building them but
in recognising which pattern a piece of code is playing, and noticing when a
responsibility has landed in the wrong one.
The vocabulary, disambiguated
"Model" means three different things in a typical discussion, and conflating them causes
real design errors:
Domain model the business objects and rules (domain-logic-organization)
Presentation model what the view needs: already formatted, already decided
Framework model the map of attributes handed to a template (Spring's Model)
The separation used here for a web boundary:
Controller interprets the request, invokes the application, selects the response.
Delegates business rules and use-case transaction ownership.
View renders. Owns no decisions beyond presentation.
Model application state/behavior in MVC's broader vocabulary;
prefer a deliberate presentation model at the response boundary.
Web MVC is not the original Smalltalk MVC: there is no observer relationship and no
long-lived view. The name persists; the mechanism is request → controller → model → render.
Page Controller and Front Controller
Page Controller one handler per page or action. Simple, local, and every
shared concern needs a shared mechanism such as filters
or composed collaborators.
Front Controller one entry point receives every request, applies shared
concerns, and dispatches to a handler. Shared concerns
exist once; the handler stays small.
Many Java web frameworks provide a Front Controller (for example Spring's DispatcherServlet),
and handler methods play the page/action role behind it. These patterns can coexist.
The practical questions are which
concerns belong in the front controller's chain and which in the handler, and whether the
chain's stages are being used correctly.
Workflow
- Check the handler's contents and runtime. Inspect routing, security configuration,
Java and framework versions first. Examples use Servlet Spring MVC;
ProblemDetail
requires Spring 6+ (Java 17+), not an implicit upgrade. A controller should bind input,
invoke application behavior, and map the result. Trace business rules and transaction
ownership; a simple read need not gain a pass-through service (layering-and-boundaries).
- Find duplicated policy. Repetition count alone does not justify indirection; shared
authorization, error mapping, correlation, tenant and envelope semantics often belong in the
chain when centralization prevents drift and preserves ordering.
- Place it at the right stage. Filter, interceptor, argument resolver, exception
handler, advice: they see different things and run at different times. Choosing wrongly
produces a concern that works until it does not.
- Extract flow decisions. If a handler decides which step comes next based on
application state, that is an Application Controller and it belongs outside the web
layer.
- Keep the response shape a deliberate decision, not the accidental serialisation of
whatever the service returned (
remote-facade-and-dto).
- Test at the right level. Handler tests should not need a database; the mapping,
validation and status codes are what the web layer is responsible for.
Decision rules
A concern applies to every request (correlation id, security context,
request logging, tenant resolution)
→ the front controller's chain: a filter, before routing.
A concern applies to a group of handlers and needs to know which handler
was selected (authorisation on an annotation, feature flags per route)
→ enabled method security for authorisation; an interceptor can
handle non-security route metadata. Keep request security in
the security filter chain, with consistent path matching.
A concern turns an exception into a response
→ consistent error mapping at each boundary: MVC advice for MVC
exceptions, security/filter/container handlers for their failures.
Share the contract; avoid duplicated generic catches.
A concern turns request data into a domain-shaped parameter
(the current user, a parsed range, a tenant)
→ an argument resolver. This removes the boilerplate without
hiding a business rule.
The next step of a multi-step flow depends on state, not on a link
→ Application Controller: a class that owns the flow, outside
the web layer, testable without HTTP.
A screen is one page, one action, no shared concerns beyond the global
ones
→ a plain handler. Do not build a flow abstraction for it.
The API is REST over resources
→ routing is by resource and method, not by page. Page
Controller and Front Controller both still describe what the
framework does; the patterns to reach for are Remote Facade
and DTO, not Two Step View.
Rules
- A controller deciding domain policy is a layering defect;
if itself is not evidence because
protocol negotiation, optional input and response mapping legitimately branch. Trace whether the
condition must hold for non-HTTP callers.
- A controller with a repository call is not automatically wrong. For a pure read it can
be the honest design; for anything that writes, it puts the transaction boundary and the
invariants in the web layer (
service-layer-design).
- Cross-cutting concerns implemented per handler can diverge. Repeated policy plus observed drift or
ordering/security risk is the signal, and
consider a shared chain stage or collaborator before a base controller: Java's single
class inheritance makes independently varying base-class policies awkward to combine.
- Choose the chain stage by what it must see. Ordinary Servlet filters run before MVC
handler selection. Interceptors see the selected handler but are not a sufficient security
boundary: Spring warns of path-matching mismatches. Use the security chain and enabled
method security; test uncovered routes and alternate dispatches.
- The framework's model map is a presentation concern. Putting entities in it couples the
template to entity properties and can trigger lazy loading during rendering
(
orm-behavioral-patterns).
- Validation splits in two and both halves are needed: syntactic (required, format,
range) belongs at the boundary, on the request type; semantic (this customer may not
order this product) belongs in the domain, where it can be enforced regardless of the
caller.
- One deliberate error contract across the application's HTTP boundaries. RFC 9457 problem details give a standard
target (
rpc-and-api-contracts).
- Application Controller is the least-known pattern here and the most useful where it
applies: multi-step flows, approval chains, state machines. Its value is that the flow
becomes a testable object rather than a set of redirects spread over handlers.
- Do not map classical page-flow patterns onto an HTTP API by analogy. An API may expose resources,
commands, workflows and hypermedia; Remote Facade is useful when network granularity requires it,
not a synonym for every REST endpoint
(
remote-facade-and-dto).
- Handler tests cover binding, validation, status codes and error shape with application
doubles where useful. Separate integration tests may legitimately include a database to
verify transaction, authorization or serialization behavior (
architecture-testing).
Return the observed responsibility/ordering issue, the proposed placement and its evidence,
and the focused checks performed or still needed. Missing configuration makes claims about
filter coverage, authorization and transaction scope conditional; inspect it before diagnosing.
References
- Page Controller versus Front Controller — both
patterns in a modern stack, exactly which shared concern belongs at which stage of the
chain (filter, interceptor, argument resolver, advice) with the ordering that matters, the
base-controller anti-pattern, and how the same reasoning applies to a message consumer or
a scheduled job. Read when placing a cross-cutting concern or reviewing a controller.
- Application Controller — flow logic extracted from
handlers: a state machine over an application process, where the flow state lives, how it
is tested without HTTP, and when a flow abstraction is overkill. Read when a wizard,
approval chain or multi-step process is being built or has become unmanageable.
1---2name: mvc-and-request-handling3description: How a web request is routed and handled: MVC's actual division of responsibilities, Page Controller versus Front Controller, and Application Controller for flows whose next step is a decision. Use when controllers contain business rules or persistence calls, when the same cross-cutting concern is copied into every handler, when a wizard's navigation logic is spread across handlers as if-chains, when a filter, interceptor and handler contend for one concern, when a controller is tested by starting the whole application, or when classical web patterns are mapped onto a REST API. Does not cover how the response is rendered (view-and-representation-patterns), the remote operation and its payload (remote-facade-and-dto), the use-case layer (service-layer-design), or where conversation state lives across requests (session-state-strategies).4---56# MVC and Request Handling78## Purpose910Keep the web layer to its actual job — turning a request into a call and a result into a11response — and put shared request concerns in one place instead of in every handler. Modern12frameworks implement these patterns for you, so the value here is not in building them but13in **recognising which pattern a piece of code is playing**, and noticing when a14responsibility has landed in the wrong one.1516## The vocabulary, disambiguated1718"Model" means three different things in a typical discussion, and conflating them causes19real design errors:2021```text22Domain model the business objects and rules (domain-logic-organization)23Presentation model what the view needs: already formatted, already decided24Framework model the map of attributes handed to a template (Spring's Model)25```2627The separation used here for a web boundary:2829```text30Controller interprets the request, invokes the application, selects the response.31 Delegates business rules and use-case transaction ownership.32View renders. Owns no decisions beyond presentation.33Model application state/behavior in MVC's broader vocabulary;34 prefer a deliberate presentation model at the response boundary.35```3637Web MVC is not the original Smalltalk MVC: there is no observer relationship and no38long-lived view. The name persists; the mechanism is request → controller → model → render.3940## Page Controller and Front Controller4142```text43Page Controller one handler per page or action. Simple, local, and every44 shared concern needs a shared mechanism such as filters45 or composed collaborators.4647Front Controller one entry point receives every request, applies shared48 concerns, and dispatches to a handler. Shared concerns49 exist once; the handler stays small.50```5152Many Java web frameworks provide a Front Controller (for example Spring's `DispatcherServlet`),53and handler methods play the page/action role behind it. These patterns can coexist.54The practical questions are **which55concerns belong in the front controller's chain and which in the handler**, and whether the56chain's stages are being used correctly.5758## Workflow59601. **Check the handler's contents and runtime.** Inspect routing, security configuration,61 Java and framework versions first. Examples use Servlet Spring MVC; `ProblemDetail`62 requires Spring 6+ (Java 17+), not an implicit upgrade. A controller should bind input,63 invoke application behavior, and map the result. Trace business rules and transaction64 ownership; a simple read need not gain a pass-through service (`layering-and-boundaries`).652. **Find duplicated policy.** Repetition count alone does not justify indirection; shared66 authorization, error mapping, correlation, tenant and envelope semantics often belong in the67 chain when centralization prevents drift and preserves ordering.683. **Place it at the right stage.** Filter, interceptor, argument resolver, exception69 handler, advice: they see different things and run at different times. Choosing wrongly70 produces a concern that works until it does not.714. **Extract flow decisions.** If a handler decides which step comes next based on72 application state, that is an Application Controller and it belongs outside the web73 layer.745. **Keep the response shape a deliberate decision**, not the accidental serialisation of75 whatever the service returned (`remote-facade-and-dto`).766. **Test at the right level.** Handler tests should not need a database; the mapping,77 validation and status codes are what the web layer is responsible for.7879## Decision rules8081```text82A concern applies to every request (correlation id, security context,83request logging, tenant resolution)84 → the front controller's chain: a filter, before routing.8586A concern applies to a group of handlers and needs to know which handler87was selected (authorisation on an annotation, feature flags per route)88 → enabled method security for authorisation; an interceptor can89 handle non-security route metadata. Keep request security in90 the security filter chain, with consistent path matching.9192A concern turns an exception into a response93 → consistent error mapping at each boundary: MVC advice for MVC94 exceptions, security/filter/container handlers for their failures.95 Share the contract; avoid duplicated generic catches.9697A concern turns request data into a domain-shaped parameter98(the current user, a parsed range, a tenant)99 → an argument resolver. This removes the boilerplate without100 hiding a business rule.101102The next step of a multi-step flow depends on state, not on a link103 → Application Controller: a class that owns the flow, outside104 the web layer, testable without HTTP.105106A screen is one page, one action, no shared concerns beyond the global107ones108 → a plain handler. Do not build a flow abstraction for it.109110The API is REST over resources111 → routing is by resource and method, not by page. Page112 Controller and Front Controller both still describe what the113 framework does; the patterns to reach for are Remote Facade114 and DTO, not Two Step View.115```116117## Rules118119- A controller deciding domain policy is a layering defect; `if` itself is not evidence because120 protocol negotiation, optional input and response mapping legitimately branch. Trace whether the121 condition must hold for non-HTTP callers.122- **A controller with a repository call is not automatically wrong.** For a pure read it can123 be the honest design; for anything that writes, it puts the transaction boundary and the124 invariants in the web layer (`service-layer-design`).125- Cross-cutting concerns implemented per handler can diverge. Repeated policy plus observed drift or126 ordering/security risk is the signal, and127 consider a shared chain stage or collaborator before a base controller: Java's single128 class inheritance makes independently varying base-class policies awkward to combine.129- **Choose the chain stage by what it must see.** Ordinary Servlet filters run before MVC130 handler selection. Interceptors see the selected handler but are not a sufficient security131 boundary: Spring warns of path-matching mismatches. Use the security chain and enabled132 method security; test uncovered routes and alternate dispatches.133- The framework's model map is a presentation concern. Putting entities in it couples the134 template to entity properties and can trigger lazy loading during rendering135 (`orm-behavioral-patterns`).136- Validation splits in two and both halves are needed: **syntactic** (required, format,137 range) belongs at the boundary, on the request type; **semantic** (this customer may not138 order this product) belongs in the domain, where it can be enforced regardless of the139 caller.140- One deliberate error contract across the application's HTTP boundaries. RFC 9457 problem details give a standard141 target (`rpc-and-api-contracts`).142- **Application Controller is the least-known pattern here and the most useful** where it143 applies: multi-step flows, approval chains, state machines. Its value is that the flow144 becomes a testable object rather than a set of redirects spread over handlers.145- Do not map classical page-flow patterns onto an HTTP API by analogy. An API may expose resources,146 commands, workflows and hypermedia; Remote Facade is useful when network granularity requires it,147 not a synonym for every REST endpoint148 (`remote-facade-and-dto`).149- Handler tests cover binding, validation, status codes and error shape with application150 doubles where useful. Separate integration tests may legitimately include a database to151 verify transaction, authorization or serialization behavior (`architecture-testing`).152153Return the observed responsibility/ordering issue, the proposed placement and its evidence,154and the focused checks performed or still needed. Missing configuration makes claims about155filter coverage, authorization and transaction scope conditional; inspect it before diagnosing.156157## References158159- [Page Controller versus Front Controller](references/page-vs-front-controller.md) — both160 patterns in a modern stack, exactly which shared concern belongs at which stage of the161 chain (filter, interceptor, argument resolver, advice) with the ordering that matters, the162 base-controller anti-pattern, and how the same reasoning applies to a message consumer or163 a scheduled job. Read when placing a cross-cutting concern or reviewing a controller.164- [Application Controller](references/application-controller.md) — flow logic extracted from165 handlers: a state machine over an application process, where the flow state lives, how it166 is tested without HTTP, and when a flow abstraction is overkill. Read when a wizard,167 approval chain or multi-step process is being built or has become unmanageable.