Continuando con nuestro propósito de aplicar conceptos de clean architecture en una arquitectura de N-capas trataremos inicialmente los siguientes temas:
- ¿Es la base de datos nuestro modelo de negocio?
- Interfaces como mecanismo de verdadera abstracción.
- ¿Cómo sobrevivir a las “CualquierCosaService”?
- ¿Debemos usar Excepciones?
- Evitando los side-effects
Nota: Para la aplicación práctica de la teoría usare como lenguaje de programación Kotlin con Spring Framework, sin embargo, puede ser aplicado a cualquier otro lenguaje orientado a objetos (o no) fácilmente .
¿Es la base de datos nuestro modelo de negocio?
La respuesta siempre será NO, la base de datos no debe ser más que un ente externo a nuestro dominio (lógica de negocio) haciendo que este dependa de sus propios modelos y no tenga nada que ver con una especificación de base de datos (SQL o noSQL)
Existe una mala costumbre generalizada de hacer girar toda nuestra aplicación entorno a la base de datos, objetos orm por doquier contaminando nuestra lógica de negocio con especificaciones de base de datos y asumiendo a toda luces que la entidad principal es el objeto de base de datos.
Debemos poder llegar a reemplazar la especificación de la DB (o dejar de usarla) de manera tan fácil como cambiar un par de archivos y ajustar unos cuantos mapper. Para lograr lo anterior nuestras bases de datos (SQL o noSQL) debemos verlas como temas de infraestructura externa y nuestra lógica de negocio no debe de conocer.
¿Cómo logramos esto? definiendo correctamente unas interfaces y un par de mapper que conviertan de objetos de base de datos a nuestros objetos de dominio (Si su aplicación es bastante compleja posiblemente no llegará a los objetos de dominio directamente, veremos esto en los siguientes post).
Mucha charla, poco código, vamos a la acción:
Demos un vistazo más a detalle de estos 3 archivos:
Tenemos una clase BookOrm (conocidas como Entity) que no es más que un orm que contiene adicional una función para convertir a nuestras clases de dominio ( nuestra infraestructura puede conocer de nuestro modelo, pero no al contrario), una interface BookRepository para hacer uso de la potencia de SpringJPA y nuestra clase BookSqlRepositoryImpl que gracias a la inversión de control pueda usar la interface BookRepository para hacer consultas predeterminadas por Spring a la DB.
Pero, ¿en qué se diferencia esto a como ya hemos visto en docena de proyectos?, detallemos la implementación de la interfaz que hace BookSqlRepositoryImpl de SearchBookExternalPort, es acá es donde comienza la magia, esta interfaz (SearchBookExternalPort) fue definida por nuestra lógica de negocio la cual definió un contrato (puerto) que debe ser implementado por un adaptador, que en este caso es BookSqlRepositoryImpl, pero fácilmente podría ser un BookRestRepositoryImpl.
Aclaremos un poco que es un puerto y un adaptador:
- Puerto: especifica un contrato a ser cumplido sin detallar el cómo.
- Adaptador: Nos da la implementación de cierto puerto (el cómo)
Si son detallistas al visualizar la imagen podrán observar que nuestra clase BookSqlRepositoryImpl no retorna nuestros objetos de orm (BookOrm) al exterior, en su lugar realiza una conversión (mapper) hacia un objeto de dominio (Book, ubicado en la carpeta service), de esta forma la lógica de negocio localizada en la carpeta Service no tendrá nada que ver con temas externos al no recibir un objeto orm.
Con lo anterior cumplimos el DIP en el momento que nuestra lógica de negocio depende de una abstracción (puerto) para obtener los datos y no dé una implementación.
Para guardar un libro ¿la lógica de negocio debe conocer nuestro ORM ?
Nuevamente NO. Nuestra lógica de negocio solo conoce a sus modelos, veamos un alcance de cómo deberíamos realizar el guardado
Con lo anterior vimos el primer tema sobre base de datos en cómo interactúa con nuestra lógica de negocio y un abre bocas sobre él trabajar con abstracciones, en las siguientes publicaciones veremos los siguientes temas.