La arquitectura de capas es un patrón de diseño de software que organiza el sistema en diferentes niveles o capas. Cada capa tiene una responsabilidad clara y definida, y las capas interactúan entre sí de manera jerárquica. Las capas superiores utilizan los servicios ofrecidos por las capas inferiores, pero las capas inferiores no dependen de las superiores.
Se caracteriza por la separación de responsabilidades, donde cada capa está encargada de una funcionalidad específica del sistema. Esto permite una mejor organización del código y facilita la mantenibilidad y escalabilidad del mismo.
Además, presenta ventajas como la reutilización de código y la posibilidad de realizar pruebas de manera más efectiva. Sin embargo, también tiene desventajas, como una posible sobrecarga en la comunicación entre capas y una complejidad adicional en la gestión de las interacciones.
Las capas básicas de la arquitectura de la aplicación web son la capa de presentación, la capa de dominio y la capa de persistencia.
La capa de presentación (en nuestro caso le llamaremos controller, ya que es dónde estarán nuestros controladores) es responsable de interactuar con el usuario. Aquí es donde se manejan las solicitudes y respuestas, y se presenta la información de una manera comprensible. En esta capa, se implementan los controladores que gestionan la lógica de la interfaz y coordinan la interacción entre el usuario y el sistema.
La capa de dominio contiene la lógica de negocio de la aplicación. Está compuesta por los servicios que procesan la información y las entidades del modelo que representan los datos fundamentales. Esta capa se encarga de las reglas de negocio y la validación de los datos antes de que sean enviados a la capa de persistencia.
La capa de persistencia se encarga de la gestión del almacenamiento de datos. Aquí se implementan los repositorios que facilitan la interacción con los datos, permitiendo realizar operaciones de creación, lectura, actualización y eliminación (CRUD) sobre las entidades del modelo.
Además de estas capas básicas, es posible incorporar más capas según las necesidades de la aplicación. Por ejemplo, se pueden incluir capas de servicio adicional para la integración con servicios externos, capas de presentación para distintos tipos de interfaces (como API REST y aplicaciones web), o capas de configuración y seguridad. Esta flexibilidad permite adaptar la arquitectura a los requisitos específicos de cada proyecto.
En el esquema anterior, ésto no se cumple, ya que, en realidad, todo depende de la última capa, la de persistencia. A lo largo de curso, ya iremos viendo mecanismos para solucionar esta situación
La capa de presentación es responsable de interactuar con el cliente y gestionar las solicitudes y respuestas de la aplicación.
Los controladores reciben las solicitudes HTTP, procesan los datos y devuelven respuestas adecuadas. Utilizan los servicios de la capa de dominio para llevar a cabo la lógica de negocio necesaria.
En Spring es habitual tener en esta capa nuestros controladores y el sistema de enrutamiento. Es común utilizar anotaciones en esta capa, como:
@RestController @RequestMapping(BookController.URL) @RequiredArgsConstructor public class BookController { public static final String URL = "/books"; private final BookService bookService; @GetMapping public List<Book> getAll() { return bookService.getAll(); } @GetMapping("/{isbn}") public Book findByIsbn(@PathVariable String isbn) { return bookService.findByIsbn(isbn); } }
Por ejemplo, los métodos getAll() y findByIsbn() manejan las solicitudes para obtener todos los libros y para encontrar un libro específico por su ISBN, respectivamente. Ambos métodos utilizan el servicio BookService para realizar la lógica de negocio, manteniendo así una clara separación entre la presentación y la lógica del dominio.
Es el componente principal de nuestra aplicación, donde estarán nuestras modelos y reglas de negocio.
Esta capa no debería tener ninguna dependencia con componentes externos, con lo que deberemos buscar mecanismos para evitar introducir referencias a Spring, bbdd, JSON…
public class Book { package es.cesguiro.model; import java.math.BigDecimal; import java.math.RoundingMode; import java.time.LocalDate; import java.util.List; public class Book { private String isbn; private String titleEs; private String titleEn; private String synopsisEs; private String synopsisEn; private BigDecimal basePrice; private double discountPercentage; private BigDecimal price; private String cover; private LocalDate publicationDate; private Publisher publisher; private List<Author> authors; // Constructores, getters, setters, lógica de negocio, validaciones... }
package es.cesguiro.service; import java.util.List; import java.util.Optional; public interface BookService { List<Book> getAll(int page, int size); //... }
package es.cesguiro.service.impl; import es.cesguiro.repository.BookRepository; import es.cesguiro.service.BookService; import java.util.List; import java.util.Optional; public class BookServiceImpl implements BookService { private final BookRepository bookRepository; public BookServiceImpl(BookRepository bookRepository) { this.bookRepository = bookRepository; } @Override public List<Book> getAll(int page, int size) { return bookRepository.findAll(page, size); } // ... }
La capa de persistencia es responsable de gestionar la interacción con el origen de datos (base de datos, ficheros, APIs…), permitiendo el almacenamiento y recuperación de los mismos.
public interface BookRepository { List<Book> getAll(); }
public BookRepositoryImpl { List<Book> getAll() { // Conectar con el origen de datos de nuestra aplicación // Recuperar y devolver listado de libros } }