Base de datos - FactoriaF5-Asturias/project-p5-digital-academy-team3-restaurant-backend GitHub Wiki

Base de datos

Modelo Entidad-Relación

USERS_AUTH 1:1 USERS_DETAILS USERS_ROLE 1:N USERS_DETAILS

TABLETS 1:N ORDERS ORDERS 1:N ORDER_ITEMS ORDERS 1:1 INVOICES ORDER_TYPE 1:N ORDERS PAYMENT_METHOD 1:N ORDERS STATUS 1:N ORDERS PRODUCTS 1:N ORDER_ITEMS PRODUCTS N:1 CATEGORIES

Tablas

  • USERS_AUTH
    • id INTEGER GENERATED ALWAYS AS IDENTITY PRIMARY KEY
    • username VARCHAR(255) NOT NULL UNIQUE
    • password_hash TEXT NOT NULL
    • created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
  • USERS_ROLE
    • id INTEGER GENERATED ALWAYS AS IDENTITY PRIMARY KEY
    • name VARCHAR(30) NOT NULL UNIQUE
  • USERS_DETAILS
    • user_id INTEGER PRIMARY KEY FK
    • email VARCHAR(60) NOT NULL UNIQUE
    • avatar_url TEXT NULL
    • role_id INTEGER NOT NULL FK
    • updated_at TIMESTAMP NULL
  • PRODUCTS: catálogo de producto
    • id SERIAL PRIMARY KEY
    • name VARCHAR(150) NOT NULL
    • description TEXT NULL
    • category_id INTEGER NOT NULL FK (categories.id)
    • price DECIMAL(10,2) NOT NULL
    • status BOOLEAN NOT NULL DEFAULT TRUE
    • image_url VARCHAR(255) NULL
  • CATEGORIES: Definir tipo de productos
    • id SERIAL PRIMARY KEY
    • name VARCHAR(50) NOT NULL UNIQUE
  • TABLETS: Tablet de la mesa donde se realizan pedidos
    • id SERIAL PRIMARY KEY
    • name VARCHAR(30) NOT NULL UNIQUE
  • STATUS: Estado de pedido
    • id SERIAL PRIMARY KEY
    • name VARCHAR(50) UNIQUE NULL
  • PAYMENT_METHOD: Tipo de pago
    • id SERIAL PRIMARY KEY
    • name VARCHAR(30) NOT NULL UNIQUE
  • ORDERS: Pedido hecho por cliente
    • id SERIAL PRIMARY KEY
    • tablet_id INTEGER NULL FK (tablets.id)
    • status_name VARCHAR(50) NULL FK (status.name)
    • order_type_name VARCHAR(30) NULL FK (order_type.name)
    • payment_method_name VARCHAR(30) NULL FK (payment_method.name)
    • total_amount DECIMAL(10,2) NULL
    • created_at TIMESTAMP NULL
  • ORDER_ITEMS: Productos, cantidades y precio de cada producto incluido en un pedido.
    • id SERIAL PRIMARY KEY
    • order_id INTEGER NOT NULL FK (orders.id) ON DELETE CASCADE
    • product_id INTEGER NOT NULL FK (products.id)
    • quantity INTEGER NOT NULL
    • unit_price DECIMAL(10,2) NOT NULL
    • subtotal DECIMAL(10,2) NOT NULL
  • ORDER_TYPE: Tipo de pedido
    • id SERIAL PRIMARY KEY
    • name VARCHAR(30) NOT NULL UNIQUE
  • INVOICES: Factura de pedido
    • id SERIAL PRIMARY KEY
    • order_id INTEGER NOT NULL UNIQUE FK (orders.id) ON DELETE CASCADE
    • invoice_number VARCHAR(50) NOT NULL
    • total_amount DECIMAL(10,2) NOT NULL
    • issued_at TIMESTAMP NULL

Diagrama patas de gallo

erDiagram
    USERS_AUTH ||--|| USERS_DETAILS : tiene
    USERS_ROLE ||--o{ USERS_DETAILS : asigna
    TABLETS o|--o{ ORDERS : recibe
    STATUS o|--o{ ORDERS : clasifica
    ORDER_TYPE o|--o{ ORDERS : define
    PAYMENT_METHOD o|--o{ ORDERS : utiliza
    ORDERS ||--|{ ORDER_ITEMS : contiene
    ORDERS ||--o| INVOICES : genera
    PRODUCTS ||--o{ ORDER_ITEMS : incluye
    CATEGORIES ||--o{ PRODUCTS : agrupa

    USERS_AUTH {
        int id PK
        varchar username UK
        text password_hash
        timestamp created_at
    }
    USERS_ROLE {
        int id PK
        varchar name UK
    }
    USERS_DETAILS {
        int user_id PK
        varchar email UK
        text avatar_url
        int role_id FK
        timestamp updated_at
    }
    TABLETS {
        int id PK
        varchar name UK
    }
    STATUS {
        int id PK
        varchar name UK
    }
    ORDER_TYPE {
        int id PK
        varchar name UK
    }
    PAYMENT_METHOD {
        int id PK
        varchar name UK
    }
    ORDERS {
        int id PK
        int tablet_id FK
        varchar status_name FK
        varchar order_type_name FK
        varchar payment_method_name FK
        decimal total_amount
        timestamp created_at
    }
    ORDER_ITEMS {
        int id PK
        int order_id FK
        int product_id FK
        int quantity
        decimal unit_price
        decimal subtotal
    }
    INVOICES {
        int id PK
        int order_id UK
        varchar invoice_number
        decimal total_amount
        timestamp issued_at
    }
    PRODUCTS {
        int id PK
        varchar name
        text description
        int category_id FK
        decimal price
        boolean status
        varchar image_url
    }
    CATEGORIES {
        int id PK
        varchar name UK
    }

Decisiones técnicas

  1. En un principio, se crearon las tablas sin pensar en los usuarios ya que para el MvP que queríamos realizar no era necesario.
  2. También decidimos delegar la persistencia de datos del carrito a frontend mediante LocalStorage ya que no es buena práctica tener datos volátiles en bases de datos relacionales.
  3. Cuando se implementaron las tablas de usuarios, dedicamos una tabla puramente a autenticación, otra para crear los roles, ya que no vimos conveniente usar un ENUM(mala práctica) y otra tabla para el resto de detalles de usuario.

Dependencia utilizada

Flyway

¿Por que?

Básicamente es una dependencia que nos permite gestionar las tablas mediante migraciones (pequeños bloques de código SQL con inserciones atómicas) y así poder tenerla versionada y fácilmente accesible, mejorando su rendimiento y escalabilidad.

Un inconveniente que encontramos en el desarrollo del proyecto fué que si queríamos realizar una modificación de las migraciones ya existentes estabas obligado a realizar una migración nueva, ya que las migraciones que ya han sido aplicadas, no pueden ser modificadas.

Esto sucede porque cuando inicializas la base de datos, y Maven lee las migraciones y las valida con JpaHibernate, se genera una tabla interna en la DB con un historial donde guarda toda la información de las migraciones llamadaflyway_schema_history. Si estas no coinciden, saltará un error que no te permitirá seguir trabajando con la base de datos.

¿Como lo solucionamos?

Una solución muy limpia que nos permitió no solo solucionar el problema, sino además poder comprimir las migraciones y poder editar las existentes sobre la marcha fué la implementación de Docker el proyecto.

Gracias a esta herramienta, no solo pudimos automatizar el arranque y cierre de la base de datos junto al servicio de backend, pudimos (en desarrollo, no en producción) borrar el volumen del contenedor (lo que equivaldría a borrar la base de datos), pudiendo así poder trabajar con las migraciones de una forma mucho mas cómoda y dinámica.