Coder Social home page Coder Social logo

dbdtp23_cardpurchases_grupo7's Introduction

dbd_tp2022-23

Trabajo final de Diseño de Bases de Datos 2022/23 - GRUPO 7 (versión SQL)

Para la versión Mongo de este documento haga clic acá

Introducción

Esta es una aplicación que expone funcionalidad mediante una API REST, la misma puede ser accedida mediante algún cliente de prueba tal como Postman, JMeter o la misma interfaz gráfica expuesta por la aplicación, lo cual se recomienda (ver la sección de documentación y prueba para más detalles). La base de datos usada es MySQL 8.0

Para simplificar el desarrollo los tests unitarios son de integración es decir que la aplicación se prueba desde la API REST misma (podría hacerse desde los servicios, pero para evitar cierta duplicación en las pruebas, se prueba directamente desde la capa más externa)

Requerimientos

  • Docker
  • Docker compose
  • Java 19
  • Maven
  • git

El desarrollo se hizo bajo Linux (openSUSE 15.4), no se probó en otras plataformas (macOS, Windows), pero debería funcionar sin problemas en ambas.

Nota: En el archivo pom.xml se agregó una tarea:

  • Start-up dependant services: Automáticamente levanta el docker de SQL mediante docker compose tanto al ejecutar la aplicación como al ejecutar sus tests unitarios.

Instalación y ejecución

$ git clone -b sql_version [email protected]:jalbiero/dbdtp23_cardpurchases_grupo7.git
$ cd dbdtp23_cardpurchases_grupo7
$ mvn spring-boot:run

Puertos TCP usados

Documentación y prueba manual de los endpoints implementados

Decisiones de desarrollo

Iniciales

  • Se usa Java 19 con habilitación de "preview features" para usar funcionalidad nueva de pattern matching, específicamente switch para instanceof (ej: ver ResponseDTO.java)
  • Se actualizaron tipos de datos discontinuados tales como:
    • Anotaciones JPA: En los ejemplos prácticos se usa javax.persistence.*, en este trabajo se usa su actualzación jakarta.persistence.*
    • Fecha: java.util.Date a java.time.LocalDate
  • Por cuestiones de claridad las siguientes clases fueron renombradas (ya que al representar compras se confundían con los pagos de las mismas)
    • CashPayment a CashPurchase
    • MonthlyPayment a CreditPurchase

Controladores y servicos

Para aislar la funcionalidad pedida de lo que se necesita para probarla se decidió dividir las capas de controladores y servicios en 2 partes:

  1. El controlador CardPurchasesController y su servicio asociado CardPurchasesService implementan solamente lo que se pide como tarea (sus tests unitarios asociados están en CardPurchasesControllerTests.
  2. El controlador TestController y su servicio asociado TestService (tests unitarios asociados en TestControllerTests) implementan funcionalidad necesaria para probar lo pedido en la tarea. En una aplicación completa lo pedido sería sólo una parte del total, el cual se complementaría con lo que está en TestController/TestService.
  3. Por cuestiones de tiempo la documentación de los endpoints se hace solamente para el controlador CardPurchasesController de una forma sencilla agregándose además swagger para listar/probar los mismos en runtime como se comentó anteriormente.

Pruebas

Para las pruebas (tanto manuales con la aplicación funcionando, como para los tests unitarios) se diseño un servicio (TestDataGeneratorService) que se ejecuta al arrancar la aplicación. La funcionalidad del mismo es generar datos de prueba (lo más real posibles) en la base de datos. Inicialmente se evaluó la opción de tener un archivo .sql, pero no iba a escalar ya que:

  1. Era muy dependiente de la estructura de datos generada por JPA (un cambio en las anotaciones y la estructura difiere)
  2. Iba a ser necesario tener un archivo similar para Mongo.

En conclusión: El código del servicio trabaja con las entidades del modelo por lo cual es casi independiente de la base de datos subyacente ("casi" ya que hay algunas pequeñas diferencias).

La generación de datos se controla mediante el archivo de propiedades de la aplicación. Por simplicidad, sobre todo para ciertos tests, y a menos que esas propiedades sean modificadas, los datos generados en cada corrida de la aplicación, o de los test unitarios, van a ser siempre los mismos.

Los tests unitarios se ejecutan con:

$ mvn test

Modelo (SQL)

En el modelo se tomaron las siguientes decisiones:

  • Se anotó cada atributo con propiedades básicas tales como:
    • Si puede ser nulo o no.
    • Longitud máxima de caracteres en caso de las cadenas.
    • Unicidad en los que se requiera (DNI, CUIT, etc)
  • En el caso de colecciones que se mapean a tablas:
    • En la mayoría de las colecciones se usó @JoinColumn (en una minoría, para un control más fino, @JoinTable) junto con @OneToMany o @ManyToMany; esto para simplificar la generación del modelo en la base. Sin @JoinColumn (@JoinTable) se generan tablas extras intermedias que no son óptimas desde el punto de vista del rendimiento.
    • Se usó además el valor por defecto para el fetch (LAZY) y para las operaciones de cascada (desabilitado) ya que no se tuvo necesidad de activar las mismas.
  • En cuanto a la herencia: Hay 2 grupos de clases que las usan, Purchase con CashPurchase y CreditPurchase, y Promotion con Financing y Discount. En ambos caso se decidió usar una estrategia de tipo InheritanceType.SINGLE_TABLE, ya que es la que mayor rendimiento tiene. Su único punto en contra es que en las clases derivadas los atributos deben ser nullable.

Otros

  • En la clase Quota, por conveniencia, se cambiaron los tipos de datos de los attributos monthy year, ambos originalmente String a int.
  • En la clase CashPurchase se agregaron los attributos monthy year para poder agrupar las compras bajo un mismo pago (al igual que las cuotas).
  • Con respecto a los DTO:
    • El mapper más simple y directo de usar es, en mi opinión, "modelmapper", pero lamentablemente no soporta objetos DTO basados en records (los cuales son muy sencillos de definir y usar)
    • Por lo dicho anteriomente, opté por hacer el mapeo de los objetos basados en entidades a DTO de manera manual (agregando un par de métodos estáticos a los DTO para la conversión entre y hace entidades del modelo).
    • Hay mucha discusión sobre en qué capa usar los DTOs, muchos a favor (y con cierta razón) que los mismos deben usarse en la capa de transporte, es decir en la de los controladores. El problema es que en esa capa muchas veces no se cuenta con datos que el servicio posee para generar correctamente el DTO. Por lo anterior tomé la decisión de que la capa de servicio devuelva resultados directamente en DTO para la capa de controladores.

Conclusión

Nota: Esta sección es la misma en SQL y Mongo.

Más allá de las conocidas diferencias entre base de datos SQL (relacionales) y bases NoSQL 1 en las que se puede ver que ambos tipos presentan fortalezas y debilidades, lo importante, desde mi punto de vista, es entender que ambas se pueden complementar perfectamente en un mismo sistema. A continuación una experiencia personal:

Uno de mis últimos trabajos fue una aplicación de citas (que todavía sigue en el mercado), en la cual en su backend actualmente se usan ambos tipos de base de datos.

Originalmente se empezó con una base de datos relacional (MySQL). Hasta el día de hoy dicha base contiene toda la información de los usuarios. La misma está estructurada en una serie de tablas donde lo importante es la consistencia y validación de los datos. Inicialmente la aplicación funcionaba bien (en lo que a velocidad respecta) debido a la poca cantidad de usuarios, así como también a cierta funcionalidad limitada.

Con el paso del tiempo, los usuarios fueron aumentando junto con las nuevas características ideadas por el área de marketing y las relevadas por el área de investigación de mercado. Dichas características incluían filtrar usuarios por todo tipo de atributos, tener filtros reversos (evitar que ciertos usuarios vean "mi" perfil), bloquear usuarios en particular, ranquear a los usuarios que se visualizan en un perfil en base a muchas condiciones tales como tiempo de conexión, completitud del perfil, ubicación geográfica, etc, etc. Todo lo anterior terminaba siendo implementado en consultas SQL que crecían en tamaño y complejidad (eran complicadas de mantener o mejorar), y además, como si fuera poco, se iban haciendo cada vez más lentas (hay que tener en cuenta que en una aplicación móvil el usuario tiene la adictiva opción, que la usa todo el tiempo, de refrescar los datos que ve en pantalla con un simple gesto (pull to refresh) por lo tanto dicha falta de velocidad era cada vez más notoria.

Ante esta situación se optó en un momento dado por mantener una vista materializada del perfil de cada usuario en una base de datos NoSQL (Elasticsearch para ser más exacto). Dicha vista era una simple colección de atributos del usuario, tanto reales como meta datos asociados para simplificar las consultas. Esto mejoró notablemente la velocidad, además de simplificar las consultas en si. Por supuesto que aparecieron escenarios de consistencia eventual, pero no fueron un problema en absoluto. Ej: un típico caso es aquel en donde un usuario actualiza su perfil en la base SQL. Esta actualización dispara un proceso que inyecta dichos datos actualizados en Elasticsearch además de generar una reindexación de los mismos. Esto lleva tiempo, por lo cual otros usuarios pueden ver el perfil "desactualizado" por cierto lapso, lo cual no es un problema ya que no saben cuando el otro usuario realmente cambió sus datos. Es como ver un evento deportivo "en vivo", pero por streaming, mientras no haya otra fuente de verdad sobre el "vivo", el delay del streaming no es percibido; lo mismo pasa en la aplicación.

En definitiva, no es una base o la otra, pueden ser ambas. La clave es conocer en que escenarios puede una base desempeñarse mejor, para, llegado el caso, usar solamente una de ellas o combinar ambas.

Footnotes

  1. ACID vs BASE, escalabilidad vertical vs horizontal, con esquema vs sin esquema (o esquema flexible), etc.

dbdtp23_cardpurchases_grupo7's People

Contributors

jalbiero avatar

Watchers

 avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.