====== 07 - Front Controller ======
Con servlets, cada ruta necesita su componente. Cuando el número de rutas crece, aparecen problemas: configuración repetida, lógica transversal duplicada (autenticación, mapeo de errores) y nomenclatura inconsistente. El patrón **front controller** concentra toda la entrada en un único punto que enruta cada petición al código que la procesa.
===== 1. El problema =====
Sin front controller:
/books -> BookServlet
/books/42 -> BookDetailServlet
/authors -> AuthorServlet
/login -> LoginServlet
Cada servlet repite: validación del token, parseo, traducción de errores.
Con front controller:
/* -> DispatcherServlet (único punto de entrada)
├── valida el token (una vez)
├── decide el controlador según la ruta
└── traduce los errores (una vez)
===== 2. Implementación con servlets =====
@WebServlet("/*")
public class DispatcherServlet extends HttpServlet {
private final BookController bookController = new BookController();
private final AuthorController authorController = new AuthorController();
@Override
protected void service(HttpServletRequest request,
HttpServletResponse response)
throws IOException {
String path = request.getRequestURI(); // /books/42
String method = request.getMethod(); // GET
if (path.startsWith("/books")) {
bookController.handle(request, response);
} else if (path.startsWith("/authors")) {
authorController.handle(request, response);
} else {
response.sendError(HttpServletResponse.SC_NOT_FOUND);
}
}
}
public class BookController {
public void handle(HttpServletRequest request, HttpServletResponse response)
throws IOException {
String method = request.getMethod();
switch (method) {
case "GET" -> find(request, response);
case "POST" -> create(request, response);
default -> response.sendError(405);
}
}
private void find(HttpServletRequest request, HttpServletResponse response)
throws IOException {
String id = extractId(request.getRequestURI()); // /books/42 -> 42
// delegar en el service, escribir la respuesta JSON
}
}
^ Componente ^ Responsabilidad ^
| ''DispatcherServlet'' | Punto de entrada único: filtra, enruta y traduce errores |
| ''BookController'' | Gestiona las operaciones de un recurso según el método HTTP |
| ''Service'' | La lógica (capas de la arquitectura por capas) |
===== 3. Lo que aporta =====
* **Punto único** para lógica transversal: token, errores, registro.
* **Rutas expresivas:** una convención única (''/books'', ''/books/42'') en lugar de un servlet por cada caso.
* **Componentes por recurso** (''controllers'') con responsabilidad clara.
* Es exactamente la estructura que automatiza **Spring MVC** con su ''DispatcherServlet'': por eso entenderlo aquí hace trivial entender Spring en 2.º.
@startuml
actor Cliente
participant "DispatcherServlet\n(front controller)" as FC
participant "Filtro de tokens" as F
participant "BookController" as BC
participant "Service" as S
Cliente -> F : petición
F -> FC : token válido
FC -> BC : /books/42, GET
BC -> S : findById(42)
S --> BC : Book
BC --> FC : JSON
FC --> Cliente : respuesta
@enduml
El front controller no sustituye a la arquitectura por capas: la completa. El dispatcher y los controladores son la capa de presentación; debajo siguen service y repositorio con sus responsabilidades.