Clean Architecture + Springboot + JPA (Parte 2)
Repositorio
Esta es la parte 2, la parte 1 es: https://jesusledesma.medium.com/clean-architecture-springboot-jpa-parte-1-458b69bdf06f
Contract Service
Ya tenemos nuestro Product que era uno de los objetos que necesitamos para generar nuestro Contract, ahora necesitamos un servicio que nos brinde un método para efectivamente crear el contrato
La clase ContractService, como es un servicio de un contrato debe estar en core/contract/service
ContractDocumentRepository
Para hacer más didáctico el tutorial guardaremos los contracts en una base de datos documental, esta bueno ver como aplicar Clean Architecture + JPA + NoSql
Comenzaremos creando una interfaz que será usada por los services y casos de usos, tal como hicimos con Product
Recibiremos un Contract en el método save para guardarlo
Reestructuración de paquetes
Si miramos la imagen anterior, podemos intuir que la estructura para nuestra base Documental será muy parecida a la de nuestra base Sql, por el cual reestructuramos los paquetes de la siguiente forma
Esta idea se podría extender a todos los tipos de bases de datos que manejemos como por ejemplo key-value, column-oriented o graph
Modelo JPA
JPA por medio de annotations nos permite a través de un ORM, mapear nuestros objetos a documentos en nuestra base de datos
Creamos un objeto ContractDocument para lograr lo mencionado
JPA Repository DAO
Al igual que con Product crearemos un DAO el cual tendrá los métodos para conectarnos con la base de datos. Esta vez en vez de extender de CrudRepository, lo haremos de MongoRepository teniendo el mismo beneficio de ahorrarnos mucho código
Contract Document Adapter
Llegó el momento de conectar nuestra “capa 2 (Servicios)” con la “capa 4 (F y D)” por medio de nuestro adapter. Podemos ver como los conceptos se repiten
Al igual que con ProductSqlAdapter, tenemos como dependencia el Dao y en mi caso ModelMapper para evitar hacer a mano el mapeo de objeto jpa a objeto de mi dominio y viceversa
Contract Service Terminado
Así luce contractService terminado, haciendo uso de todo lo implementado
Contract Controller Terminado
Recordemos que anteriormente el controller quedó solo usando ProductService para obtener el producto, ya que lo necesitábamos para crear un contrato. Un Contract al crearse se le genera un id que sumado a los templatesNames que obtenemos del producto, logramos generar un ContractOutput que es el objeto de salida del contrato
Este es el código final de ContractController
O una alternativa del mismo código un poco más funcional
Inyección de dependencias
Spring Config
Con spring estamos acostumbrados a usar @Autowire en todos nuestros servicios para que se inyecten los beans, y no estar haciendo a mano los new de cada clase. Pero usar estas annotations en nuestra capa 2 (Servicios) nos acopla a un framework, por eso la inyección de dependencias y creaciones de beans estarán en la capa 4 (F y D)
Lo primero que haremos es crear un nuevo paquete llamado application el cual contendrá la clase SpringConfig
Esta clase es la encargada de crear los beans que nuestro framework necesita y luce así
Importante
Algo conceptual que tenemos que entender en que en este caso la clase se llama SpringConfig, lo cual significa que es una configuración diseñada para este framework, pero en este paquete podriamos tener configuraciones para más de un framework o inclusive ninguno. Es una de las ventajas de usar clean-architecture. Que con unas simples configuraciones tenemos mucha más flexibilidad sin necesidad de cambiar la “capa 1 (Dominio)” y “capa 2 (Servicios)” de nuestras aplicacion
Manual Config
Spring no es la única manera de manejar nuestras dependencias, existen más posibilidades para todos los gustos
Existe una postura que expone no tener ningún framework en nuestra app, y manejarse solo con librerías, ya que los frameworks nos obligan a trabajar a su forma, por el cual no tenemos el 100% del control. Solo usariamos librerías para obtener features sin necesidad de codearlos.
Es una postura válida, en mi opinión, para lenguajes que no tienen un super framework como sucede con spring. Un ejemplo es Scala donde mucha gente no quiere usar Play!
Pero sinceramente para mi, si usamos clean-architecture, nos abstraemos del framework y estaría bueno aprovechar sus features sin acoplarnos gracias a las capas que manejamos
En el caso de nuestra app si usaramos una config manual, sería un código bastante parecido aunque enumerare las diferencias:
- Crearemos las clases, en base a realizar varios new de objetos
- Tendríamos métodos al igual que SpringConfig, para usar en la Clase Main
- No usariamos las annotations de @configuration, @bean, etc
- No intanciariamos nunca RestTemplate con el restTemplate de spring, si no que nuestra manera de realizar consultas web sería a partir del objeto de alguna librería o de forma nativa en java o con httpClient de java 9. O directamente usar RestTemplates en todos los casos a pesar de no usar el framework (https://stackoverflow.com/questions/39868792/using-spring-resttemplate-in-jax-rs-project)
- Deberíamos obtener las variables de configuración como las de aplication.properties (RiskUrl) de otra forma, ya que no existiría @Value, o directamente hardcodeadas en el código cosa que no recomiendo
- Los Dao no se obtendrían con @Autowire, ya que no serían una clase de JPA, si no una clase común de java, que realiza la conexión con la base de datos a partir de un driver, por ejemplo el driver de mySql o de hazelcast. Los Dao se instanciarian con new, al igual que todas las demás dependencias
Guice Config
Existe una tercera postura, también válida. Hace referencia a la idea de manejar nuestras app con la mayor cantidad de librerías posibles, y en el caso de necesitar un framework, que estos sean pequeños y cohesivos, es decir que cumpla una sola función (A diferencia de Spring que tiene muchas como manejo web, dependencias, jpa, etc)
Surgió a partir de criticar como un framework grande (spring por ejemplo), llena el pom de un montón de subdepencias las cuales muchas veces generan conflictos de versión con otras
Y dada situaciones como estas, se planteó no ser tan extremista como el caso anterior, es decir, si usar frameworks pero que sean puntuales y que hagan solo una cosa. En el caso de inyección de dependencias uno de los más conocidos en Guice de Google
Dejo un link para que puedan chequear cómo se usa https://www.baeldung.com/guice
Main
Reestructuración de paquetes
Una cosa es la clase “Main” punto de entrada de nuestro proyecto y otra cosa son las clases de configuración de dependencias. Hacemos esta distinción para tener un código más desacoplado y más claro. Por el cual podemos agrupar nuestras clases de configuración en un paquete y nuestras clases de arranque en otro
Spring Run
En el caso de la configuración de spring, debemos usar las annotations correspondientes para habilitar los repositorios Sql (JPA), Documentales (Mongo) y entidades que estos usan
Esta es nuestra clase de arranque de Spring, pero recordemos que si no usaramos spring, tendríamos otras clases dentro del paquete Run que corresponderian a las ideas mencionadas anteriormente en la sección de inyeccion de dependencias
Ahora solo falta configurar nuestros endpoint y nuestra aplicación estará 100% funcional
Web
Spring Controller
Lo primero que debemos hacer es crear un nuevo paquete dentro de application, lo llamaremos web. En este paquete están los controllers de spring, de otros frameworks o librerías nativas como javax RS
Usaremos @Autowire para inyectar el bean de ContractController que declaramos en un método de la clase de configuración de Spring
Y por medio de @PostMapping configuraremos el endpoint de nuestra spring app
Importante
Si no usaramos spring en el paquete web tendríamos otras clases que configurarian nuestros endpoints por ejemplo
- Si quisieramos usar un pequeño framework como SparkFramework tendriamos una clase llamada SparkController
https://sparkjava.com/documentation#getting-started - Si no quisieramos usar ningún framework, y quisieramos usar el lenguaje nativo de java podriamos usar httpClient de java9
https://www.baeldung.com/java-9-http-client - Otra opcion nativa de java es jax-rs https://www.oscarblancarteblog.com/api-rest-java-jax-rs
Parte 3
En los próximos capítulos veremos:
- Implementaciones de config manuales. Implementación manual de un Dao con un driver de H2. Implementación de inyección de dependencias manual. Parte web con java nativo usando java RS
- Implementaciones usando pequeñas librerías y pequeños frameworks. Parte Web con un pequeño framework llamado spark. Inyección de dependencias con Guice
- Cómo implementar una UI muy simple usando thymeleaf de Spring sin acoplarnos al framework