Este proyecto es una API REST desarrollada con Spring Boot. Proporciona una estructura organizada para gestionar datos mediante un sistema de modelos, controladores, servicios y repositorios.
Este es el paquete principal del proyecto, que contiene las siguientes carpetas clave:
-
Contiene clases de configuración para la aplicación.
-
Aquí se pueden definir configuraciones de seguridad, CORS y otros aspectos globales del proyecto.
-
📄 SwaggerConfig.java: Configura Swagger para generar documentación automática de la API. Swagger proporciona una interfaz gráfica para visualizar y probar los endpoints de la API.
- Puedes acceder a la documentación interactiva de la API a través de Swagger UI en
http://localhost:8080/swagger-ui/.
Para ver el código completo de la configuración de Swagger, consulta el archivo SwaggerConfig.java en GitHub.
- Puedes acceder a la documentación interactiva de la API a través de Swagger UI en
-
Almacena constantes globales utilizadas en toda la aplicación.
-
Útil para evitar valores "hardcoded" en el código.
-
📄 Type.java: Este archivo define un enumerador (
enum) que contiene tres tipos:COMMON,FREQUENT, yPOPULAR. Cada uno tiene un nombre asociado que se puede obtener mediante el métodogetName(). Estos valores se utilizan para categorizar ciertos elementos dentro de la aplicación.Para ver el código completo del enumerador, consulta el archivo Type.java en GitHub.
- Contiene las clases que representan las entidades de la base de datos.
- Cada modelo usa anotaciones de JPA (
@Entity,@Id,@GeneratedValue, etc.) para mapear la base de datos. - Estos modelos son utilizados para crear, leer, actualizar y eliminar datos en la base de datos mediante el uso de JPA y Hibernate.
A continuación, se describen algunos de los modelos más importantes:
-
📄 Client.java: Representa a un cliente en el sistema. Cada cliente tiene un nombre, apellido, correo electrónico, tipo de usuario y una lista de pedidos (
orders). -
📄 Dish.java: Representa un plato del menú. Incluye el nombre del plato, su descripción, precio y tipo de plato. Además, tiene una relación con el menú al que pertenece (
menu) y con los detalles del pedido (orderDetails). -
📄 Menu.java: Representa un menú que agrupa varios platos. Cada menú tiene un nombre, una descripción y una lista de platos asociados (
dishes). -
📄 Order.java: Representa un pedido realizado por un cliente. Contiene la fecha del pedido, el precio total y una lista de detalles del pedido (
orderDetails). -
📄 OrderDetail.java: Representa los detalles de un pedido, es decir, un plato específico y la cantidad que fue ordenada. Contiene el precio unitario y el subtotal del plato.
💡 Ejemplo de un modelo (Client.java):
@Entity
@Table(name = "clients")
public class Client {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String lastName;
private String email;
private String userType = Type.COMMON.getName();
private Float adjust = 1f;
@OneToMany(mappedBy = "client", cascade = CascadeType.ALL)
private List<Order> orders;
public Client(Long id, String name, String lastName, String email) {
this.id = id;
this.name = name;
this.lastName = lastName;
this.email = email;
}
public Client() {
}
}- Contiene clases para la transferencia de datos (DTOs - Data Transfer Objects).
- Los DTOs se utilizan para estructurar la información enviada y recibida en los endpoints de la API REST.
- Estos objetos permiten separar la lógica de negocio de la representación de los datos en las respuestas y peticiones, y se usan para mapear los datos de las solicitudes HTTP a objetos Java.
-
📄 ClientDTO.java: Representa un cliente con los campos
name,lastNameyemail. Es utilizado para realizar las peticiones HTTP al crear o actualizar un cliente. -
📄 DishDTO.java: Representa un plato con los campos
name,description,priceymenuId. Es utilizado para realizar las peticiones HTTP al crear o actualizar un plato en el menú. -
📄 MenuDTO.java: Representa un menú con los campos
nameydescription. Es utilizado para realizar las peticiones HTTP al crear o actualizar un menú. -
📄 OrderDTO.java: Representa un pedido realizado por un cliente, con el campo
clientId(que identifica al cliente) yorderDetails(una lista de detalles del pedido). Es utilizado para realizar peticiones HTTP relacionadas con la creación de un pedido. -
📄 OrderDetailDTO.java: Representa un detalle de un pedido, con los campos
dishId(que identifica al plato) yquantity(la cantidad del plato). Es utilizado dentro del DTO deOrderDTOpara especificar los detalles de los platos en un pedido.
📄 MessageDTO.java: Este DTO se utiliza para estructurar las respuestas de la API. Es utilizado para enviar mensajes de respuesta junto con los detalles de los resultados de una operación.
- Contiene un mensaje (
message) que describe el resultado de la operación y un campo opcional (details) que puede contener información adicional sobre el resultado. - Se utiliza para devolver respuestas estandarizadas en las peticiones HTTP, como éxito, error, o mensajes de validación.
💡 Ejemplo de MessageDTO:
@Getter
@Setter
@NoArgsConstructor
public class MessageDTO {
private String message;
private Object details;
public MessageDTO(String message, Object details) {
this.message = message;
this.details = details;
}
public MessageDTO(String message) {
this.message = message;
}
}- Contiene las interfaces que permiten interactuar con la base de datos usando Spring Data JPA.
- Se extiende de
JpaRepository<Tipo, ID>.
-
📄 IClientRepository.java:
- Proporciona los métodos necesarios para interactuar con la entidad
Client(Cliente). - Ver código en GitHub
- Proporciona los métodos necesarios para interactuar con la entidad
-
📄 IDishRepository.java:
- Proporciona los métodos necesarios para interactuar con la entidad
Dish(Plato). - Ver código en GitHub
- Proporciona los métodos necesarios para interactuar con la entidad
-
📄 IMenuRepository.java:
- Proporciona los métodos necesarios para interactuar con la entidad
Menu(Menú). - Ver código en GitHub
- Proporciona los métodos necesarios para interactuar con la entidad
-
📄 IOrderDetailRepository.java:
- Proporciona los métodos necesarios para interactuar con la entidad
OrderDetail(Detalle de Pedido). - Incluye una consulta personalizada
findByDishId(Long dishId)para obtener los detalles de los pedidos de un plato específico. - Ver código en GitHub
💡 Ejemplo de
IOrderDetailRepository:public interface IOrderDetailRepository extends JpaRepository<OrderDetail, Long> { List<OrderDetail> findByDishId(Long dishId); }
- Proporciona los métodos necesarios para interactuar con la entidad
-
📄 IOrderRepository.java:
- Proporciona los métodos necesarios para interactuar con la entidad
Order(Pedido). - Incluye una consulta personalizada
countByClient_Id(Long clientId)para contar la cantidad de pedidos de un cliente específico. - Ver código en GitHub
💡 Ejemplo de
IOrderRepository:public interface IOrderRepository extends JpaRepository<Order, Long> { Long countByClient_Id(Long clientId); }
- Proporciona los métodos necesarios para interactuar con la entidad
- Contiene la lógica de negocio de la aplicación.
- Se organiza en subcarpetas según la entidad a la que pertenece cada servicio.
- Implementa los principios SOLID y varios patrones de diseño que se detallan a continuación:
Esta carpeta contiene las interfaces que definen los contratos para los diferentes patrones de diseño implementados en los servicios:
-
📄 ICommand.java: Define el contrato básico para el patrón Command sin parámetros.
public interface ICommand<T> { T execute(); }
-
📄 ICommandParametrized.java: Extiende el patrón Command para aceptar un parámetro.
public interface ICommandParametrized<T, R> { T execute(R parameter); }
-
📄 ICommandModification.java: Especialización del patrón Command para operaciones de modificación que requieren un ID y un valor.
public interface ICommandModification<T, S> { T execute(Long id, S value); }
-
📄 IObserver.java: Define el contrato para el patrón Observer, permitiendo a los objetos recibir notificaciones de cambios.
public interface IObserver { void update(Order order); }
Implementa servicios relacionados con la entidad Cliente utilizando el patrón Command:
- 📄 CreateClient.java: Implementa
ICommandParametrizedpara crear un nuevo cliente. - 📄 GetClient.java: Implementa
ICommandParametrizedpara obtener un cliente por ID. - 📄 GetAllClients.java: Implementa
ICommandpara obtener todos los clientes. - 📄 UpdateClient.java: Implementa
ICommandModificationpara actualizar un cliente existente. - 📄 DeleteClient.java: Implementa
ICommandParametrizedpara eliminar un cliente. - 📄 UpdateTypeClient.java: Implementa
IObserverpara actualizar el tipo de cliente basado en la cantidad de pedidos.
💡 Ejemplo de implementación (CreateClient.java):
@Service
public class CreateClient implements ICommandParametrized<Client, ClientDTO> {
private final IClientRepository clientRepository;
@Autowired
public CreateClient(IClientRepository clientRepository) {
this.clientRepository = clientRepository;
}
@Override
public Client execute(ClientDTO clientDTO) {
Client client = ClientConverter.convertDtoToEntity(clientDTO);
return clientRepository.save(client);
}
}Implementa servicios relacionados con la entidad Plato:
- 📄 CreateDish.java, GetDish.java, GetAllDishes.java, UpdateDish.java, DeleteDish.java: Implementan los patrones Command para operaciones CRUD.
- 📄 UpdateTypeDish.java: Implementa
IObserverpara actualizar el tipo de plato basado en la cantidad de veces que ha sido ordenado.
Implementa servicios relacionados con la entidad Menú utilizando el patrón Command para operaciones CRUD:
- 📄 CreateMenu.java, GetMenu.java, GetAllMenu.java, UpdateMenu.java, DeleteMenu.java
Implementa servicios relacionados con la entidad Pedido, combinando los patrones Command y Observer:
-
📄 Observable.java: Clase abstracta que implementa la funcionalidad básica del patrón Observer.
public abstract class Observable { private List<IObserver> observers = new ArrayList<>(); public void addObserver(IObserver observer) { observers.add(observer); } public void notifyObservers(Order order) { for (IObserver observer : observers) { observer.update(order); } } }
-
📄 CreateOrder.java: Extiende
Observablee implementaICommandParametrizedpara crear pedidos y notificar a los observadores. También utiliza el patrón Chain of Responsibility para aplicar descuentos.
Implementa servicios relacionados con los detalles de pedidos:
- 📄 CreateOrderDetail.java: Crea detalles de pedidos.
- 📄 UpdateOrderDetail.java: Actualiza detalles de pedidos existentes.
Se usa para encapsular solicitudes como objetos, permitiendo la parametrización de clientes con diferentes solicitudes. Cada operación CRUD se implementa como un comando separado, lo que facilita la extensibilidad y el mantenimiento del código.
Se utiliza para reaccionar a cambios en los datos sin modificar directamente las clases afectadas. En este proyecto, cuando se crea un pedido (CreateOrder), se notifica a los observadores (UpdateTypeClient y UpdateTypeDish) para que actualicen el tipo de cliente y plato según corresponda.
Implementado para manejar flujos de validación y procesamiento de datos mediante una cadena de responsabilidades. Se utiliza en la aplicación de descuentos según el tipo de cliente, donde cada manejador (CommonClient, FrequentClient) decide si procesa la solicitud o la pasa al siguiente manejador en la cadena.
- Contiene las clases que manejan las solicitudes HTTP y exponen los endpoints de la API.
- Cada controlador usa
@RestControllery@RequestMappingpara definir las rutas de la API. - Estos controladores interactúan con los servicios correspondientes y devuelven respuestas adecuadas a las solicitudes HTTP.
📄💡 Ejemplo de controlador (ClientController.java):
Enlace al archivo: ClientController.java
Este controlador gestiona las operaciones CRUD (crear, obtener, actualizar y eliminar) para los clientes. Los métodos expuestos son:
- POST
/api/clients: Crea un nuevo cliente. Devuelve un mensaje de éxito junto con la información del cliente creado. - GET
/api/clients/{clientId}: Obtiene la información de un cliente por su ID. - GET
/api/clients: Obtiene la lista de todos los clientes. - PUT
/api/clients/{clientId}: Actualiza la información de un cliente específico. - DELETE
/api/clients/{clientId}: Elimina un cliente por su ID.
📄💡 Ejemplo del controlador GlobalExceptionHandler.java:
Enlace al archivo: GlobalExceptionHandler.java
El controlador GlobalExceptionHandler maneja excepciones globales en la aplicación. Usa @RestControllerAdvice para definir un controlador de excepciones globales. En este caso, se maneja específicamente la excepción RuntimeException. Cuando se lanza una RuntimeException, el manejador devuelve un objeto MessageDTO con un mensaje de error y un código HTTP 404 (Not Found).
-
💡 Manejo de excepciones: Si ocurre una
RuntimeException, el métodohandleRuntimeExceptioncaptura la excepción y devuelve un mensaje con el texto de la excepción.@RestControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler(RuntimeException.class) @ResponseStatus(HttpStatus.NOT_FOUND) public MessageDTO handleRuntimeException(RuntimeException ex) { return new MessageDTO(ex.getMessage()); } }
Este controlador garantiza que las excepciones no controladas sean manejadas de manera centralizada, proporcionando respuestas consistentes para los errores en toda la API.
**Detalles de otros controladores: **
- 📄 DishController.java: Maneja las operaciones CRUD para los platos del restaurante.
- 📄 MenuController.java: Gestiona las operaciones CRUD para los menús del restaurante.
- 📄 OrderController.java: Gestiona las operaciones CRUD para los pedidos del restaurante.
Todos estos controladores siguen un patrón similar, asegurando que cada recurso (cliente, plato, menú, pedido) tenga sus propias rutas y métodos para interactuar con ellos
Esta carpeta contiene clases con métodos utilitarios reutilizables en varias partes del proyecto. Incluye:
converters/: Contiene clases encargadas de convertir objetos DTO (Data Transfer Object) en entidades del modelo de datos.prices/: Implementa el patrón de diseño Chain of Responsibility para aplicar descuentos según el tipo de cliente.
Las clases en esta carpeta permiten convertir objetos DTO a entidades del modelo de datos. Esto es útil para mantener la separación de responsabilidades y facilitar la manipulación de datos en el sistema.
Enlaces a los conversores:
- 📄 ClientConverter.java
- 📄 DishConverter.java
- 📄 MenuConverter.java
- 📄 OrderConverter.java
- 📄 OrderDetailConverter.java
Esta carpeta implementa el patrón Chain of Responsibility para aplicar descuentos en función del tipo de cliente. Se define una jerarquía de Handlers que procesan las solicitudes de descuento de manera encadenada.
💡 Ejemplo de Implementación
Clase Base: Handler.java
public abstract class Handler {
protected Handler nextHandler;
public void setNextHandler(Handler nextHandler) {
this.nextHandler = nextHandler;
}
public abstract void handlerRequest(Order order);
}⚡ Explicación:
Handleres una clase abstracta que define un manejador con una referencia a otroHandler(nextHandler).- La implementación concreta de
handlerRequestse define en las subclases.
Manejador para Clientes Comunes: CommonClient.java
public class CommonClient extends Handler {
@Override
public void handlerRequest(Order order) {
if (nextHandler != null && !order.getClient().getUserType().equals(Type.COMMON.getName())) {
nextHandler.handlerRequest(order);
}
}
}⚡ Explicación:
- Si el cliente no es de tipo
COMMON, la solicitud pasa al siguiente manejador.
Manejador para Clientes Frecuentes: FrequentClient.java
public class FrequentClient extends Handler {
@Override
public void handlerRequest(Order order) {
if (order.getClient().getUserType().equals(Type.FREQUENT.getName())) {
order.setTotalPrice(order.getTotalPrice() * order.getClient().getAdjust());
}
}
}⚡ Explicación:
- Si el cliente es de tipo
FREQUENT, se aplica un ajuste al precio total del pedido.
Contiene archivos de configuración y recursos estáticos:
application.properties: Archivo donde se configuran los parámetros de conexión a la base de datos y la configuración de JPA/Hibernate.
⚙️ Ejemplo de configuración:
spring.application.name=restaurant
# JPA/Hibernate configuration
spring.jpa.hibernate.ddl-auto=update
# MySQL connection configuration using environment variables
spring.datasource.url=jdbc:mysql://${HOST}:${PORT}/${NAME}?createDatabaseIfNotExist=true
spring.datasource.username=${USER}
spring.datasource.password=${PASSWORD}
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver- Contiene las pruebas unitarias y de integración del proyecto.
Clona el repositorio y accede a la carpeta del proyecto:
git clone https://github.com/bymarium/07-api-rest.git
cd 07-api-rest- Asegúrate de tener MySQL instalado y en ejecución.
- Modifica el archivo
application.propertiesubicado ensrc/main/resources/con las credenciales y datos de tu base de datos.
Ejemplo:
spring.datasource.url=jdbc:mysql://localhost:3306/nombre_base_datos
spring.datasource.username=usuario
spring.datasource.password=contraseñaEste proyecto utiliza Gradle por defecto, pero también puedes ejecutarlo con Maven si lo prefieres.
./gradlew bootRunSi usas Windows:
gradlew.bat bootRunmvn spring-boot:runUna vez que la aplicación esté en ejecución, puedes acceder a la API en:
http://localhost:8080
La aplicación usa Swagger para la documentación de la API, puedes verificar los endpoints en:
http://localhost:8080/swagger-ui.html
Puedes probar la API importando una colección en Postman y enviando solicitudes a los siguientes endpoints:
- Obtener todos los clientes:
GET http://localhost:8080/api/clients - Obtener un cliente:
GET http://localhost:8080/api/clients/{id} - Crear un cliente:
POST http://localhost:8080/api/clients
{
"name": "John",
"lastName": "Doe",
"email": "johndoe@example.com"
}- Actualizar un cliente:
PUT http://localhost:8080/api/clients/{id}
{
"name": "John",
"lastName": "Doe",
"email": "john.doe@example.com"
}- Eliminar un cliente:
PUT http://localhost:8080/api/clients{id}
Este proyecto sigue la arquitectura MVC en Spring Boot, facilitando la organización del código y la escalabilidad. Además, implementa patrones de diseño para mejorar la mantenibilidad y flexibilidad. Si necesitas más detalles o colaboración, siéntete libre de contribuir al repositorio.