Front Controller Pattern
One handler receives every web request, runs the shared work once, and dispatches to the right command. Know how it differs from Page Controller, why filter order matters, and how Spring, Express and Django all build on it.
Overview
The Front Controller is one object that receives every request to a web application. It does the work that every request needs (checking who the caller is, logging, picking a language, checking a CSRF token) and then hands the request to a smaller piece of code that knows how to serve that one URL. Martin Fowler catalogs it in Patterns of Enterprise Application Architecture (2002) and describes it as a controller that handles all requests for a web site; in his version the shared handler passes request-specific work to command objects.
The problem it fixes is duplication at the edge. If every page has its own entry point, every page also has its own copy of the sign-in check and the logging. Sooner or later one copy is missing or out of date, and that page is the one with the security hole.
Interviewers use the pattern to check three things: whether you can contrast it with Page Controller, whether you know where cross-cutting work goes and why its order matters, and whether you recognize the pattern inside the frameworks you already use.
The shape
- Front controller — The one entry point; owns the request algorithm — Spring's DispatcherServlet, an Express app
- Filter chain — Shared work before and after the handler, in a fixed order — Session, authentication, logging middleware
- Router (dispatcher) — Maps method and path to one handler or command — A route table, Django's urlpatterns
- Handler (command) — The work for one action, nothing shared — get_order(request)
Front Controller versus Page Controller
Fowler's Page Controller is the other option: one object handles the request for one page or one action. Each page is its own entry point, which is simple when there are few pages and little shared work.
- Entry points — One per page or action — One for the whole site
- Shared work (auth, logging) — Repeated in each page, or inherited from a base class — Done once, before dispatch
- Adding a page — Add a file; the web server finds it — Add a route to the table
- Changing a rule for every page — Touch every page, or the base class — Touch one filter
- Where it fits — A few pages with little in common — Many URLs that share security and policy
The Core J2EE Patterns catalog from Sun (now kept by Oracle) adds a useful point: a front controller centralizes handling but is not a Singleton. An application may have more than one, each mapped to its own set of URLs, for example one for the web pages and one for the API.
One request, step by step
Two details are worth saying out loud. The logging filter sits outside the auth filter, so it records the 401 as well as the 200. And the auth filter returns early when there is no token: nothing deeper in the chain, including the handler, ever sees that request.
Cross-cutting work as a filter chain
A front controller that did all the shared work in one method would turn into a long, fragile function. The usual answer is to keep the controller small and put each concern in its own filter, then run the filters in a chain. Sun's Core J2EE Patterns name this Intercepting Filter: pluggable filters that pre-process the request and post-process the response, added or removed without touching the core request code, and run in a configured order. The same catalog lists Front Controller as the related pattern that handles the core processing, while filters handle the wrapping.
- Intercepting Filter — Each concern is a separate filter; a chain runs them in order before and after the target — This page
- Chain of Responsibility (GoF, 1994) — Each link decides to handle the request or pass it on; a filter that returns 401 is a link that stops the chain — Chain of Responsibility
- Decorator (GoF, 1994) — Each filter wraps the next and can act on the way in and on the way out — Decorator Pattern
Django's middleware documentation explains the wrapping with an onion: each middleware is a layer around the view, the request passes inward through every layer and the response passes back out in reverse order. A layer that returns a response without calling the next one short-circuits: no inner layer, and not the view, sees the request.
> [!IMPORTANT] > Order is part of the design. Django's docs give the standard example: AuthenticationMiddleware stores the user in the session, so it must run after SessionMiddleware. A filter list is code, and reordering it changes behavior.
A runnable mini front controller
The example has the four parts from the table above: handlers, a route table with path parameters, a middleware chain built by wrapping, and a dispatcher that answers 404 for an unknown path and 405 for a known path with the wrong method. No framework is imported.
Adding a page is one line in ROUTES. Adding a rule for every page is one function in the middleware list. Neither change touches the handlers.
The same idea in real frameworks
- Spring MVC — DispatcherServlet, which the docs call a central Servlet built on the front controller pattern — Servlet Filters, plus interceptors attached by the HandlerMapping for pre- and post-processing — HandlerMapping finds the handler; HandlerAdapter invokes it
- Express — The app; the docs describe an application as a series of middleware calls — app.use(...) functions that call next() or end the response — app.get(...), express.Router()
- Django — Its request handler, which runs every request through the MIDDLEWARE list — Middleware classes, applied top-down on the way in — ROOT_URLCONF and urlpatterns, tried in order, first match wins
Spring's documentation is explicit: the DispatcherServlet provides a shared algorithm for request processing, while the actual work is done by configurable delegate components (HandlerMapping, HandlerAdapter, HandlerExceptionResolver, ViewResolver, LocaleResolver and others). Express and Django do not use the name, but the structure is the same: one entry point, an ordered chain, a URL table.
Where it goes wrong
- God dispatcher — One function with a long if/elif on paths that also checks auth and formats errors — A route table plus one filter per concern
- Wrong filter order — Auth runs before the session is loaded, or logging sits inside auth and never records rejected requests — Write the order down, keep it in one list, test it
- Business logic in a filter — A filter that applies discounts or changes order state — Filters handle policy shared by all URLs; rules live in handlers and the domain
- Filters that talk to each other — Filters pass large state through request attributes — Keep each filter independent; the J2EE catalog lists costly information sharing as this pattern's main drawback
- Silent fall-through — An unknown path returns 200 with an empty body, or a framework default page — An explicit 404 at the end of the router, and 405 for a known path with the wrong method
From one application to many services
An API Gateway is the same idea one level up: microservices.io describes it as the single entry point for all clients, routing requests to services and often doing security checks such as verifying that the caller is authorized. The front controller routes to handlers inside one process; the gateway routes to whole services over the network.
What you will be able to do
- Define the Front Controller in one sentence and name the duplication it removes.
- Contrast it with Page Controller and say when each fits.
- Draw a request passing through filters, the router and one handler, including a short-circuit.
- Explain why filter order matters, with the session-before-auth example.
- Point to the front controller in Spring, Express and Django.
- Build a small front controller with a route table, a middleware chain, and 404 and 405 paths.
Interview questions
- What is the Front Controller pattern, and how is it different from Page Controller?
- Where do cross-cutting concerns go, and why does the order of filters matter?
- How does Spring MVC implement the Front Controller?
- How do you stop a front controller from becoming a god object?
- How should a front controller handle unknown routes and errors?
Sign up free to read the full topic
More Low-Level Design topics
Start free on Cariara · Coding problems · System design studio