Cómo construimos un ERP multiempresa con Next.js y Supabase
Agora Marine es un ERP que usan a diario marinas, clubs náuticos, empresas de chárter, talleres navales y asociaciones, cada una con sus datos separados. Lo construimos con Next.js, React, TypeScript y Supabase (PostgreSQL), desplegado en Vercel con infraestructura europea. Este artículo cuenta las decisiones de arquitectura que más nos han importado, incluidas las que aprendimos a la fuerza.
¿Por qué Next.js y Supabase para un ERP?
Porque un ERP moderno no es solo una pantalla de gestión: también tiene webs públicas, portales de cliente, pagos y procesos automáticos. Con Next.js servimos todo eso desde un mismo código, y Supabase nos da una base de datos PostgreSQL completa, con autenticación y almacenamiento de archivos incluidos.
PostgreSQL pesa más en la decisión que cualquier librería de interfaz. Un ERP vive de relaciones entre datos (clientes, facturas, contratos, reservas), y una base de datos relacional seria, con reglas de acceso por fila, es la base de todo lo demás.
¿Cómo se separan los datos de cada empresa?
Cada registro pertenece a una empresa, y la base de datos impide que una empresa vea los datos de otra. Lo resolvemos con reglas de seguridad a nivel de fila en PostgreSQL: el filtro no depende de que el programador se acuerde de ponerlo en cada consulta, lo aplica la propia base de datos.
Además, cada empresa puede tener su propio dominio o subdominio. El sistema traduce el dominio de cada visita a la empresa correspondiente y guarda esa traducción unos minutos en memoria para no consultar la base de datos en cada petición.
¿Cómo funciona el acceso cuando una persona trabaja con varias empresas?
Este fue uno de los problemas más interesantes. En el sector náutico es habitual que la misma persona sea cliente de una marina, socia de un club y alquile un barco a una empresa de chárter, y las tres usan Agora Marine.
La solución fue separar tres conceptos:
- La persona o entidad: su identidad (nombre, email).
- Su forma de acceder: una persona puede entrar con email y contraseña, con Google o con Microsoft.
- Su relación con cada empresa: sus datos fiscales y su papel (cliente, socio, empleado) son distintos en cada una.
Así, el mismo email funciona en todas las empresas, y un cliente que la marina dio de alta antes de que tuviera cuenta queda vinculado automáticamente la primera vez que entra.
¿Qué supone integrar VeriFactu en un ERP propio?
VeriFactu (Real Decreto 1007/2023) obliga a que cada factura quede registrada, encadenada con la anterior y firmada electrónicamente, y permite enviarla a la Agencia Tributaria. En la práctica implica:
- Generar el registro de cada factura en el formato XML oficial.
- Firmarlo electrónicamente con el certificado de la empresa.
- Enviarlo a la AEAT por su servicio web y guardar la respuesta.
- Mantener la cadena: cada registro incluye la huella del anterior, así que una factura no se puede borrar ni alterar sin que se note.
Lo tenemos en producción desde 2025. La lección principal: VeriFactu no es un módulo que se añade al final. Condiciona cómo se numeran, rectifican y anulan las facturas en todo el sistema.
¿Qué límites técnicos nos encontramos?
Uno poco conocido: las plataformas de despliegue limitan el número de rutas de una aplicación. En Vercel ese límite está en torno a 2.048, y en Next.js cada función de servidor que se puede llamar desde el navegador cuenta como una ruta. Con un ERP de decenas de módulos, nos acercamos al límite.
Lo resolvimos con disciplina de código:
- Solo exponemos como funciones de servidor las que de verdad se llaman desde la pantalla.
- La lógica interna vive en archivos marcados como «solo servidor», que no generan rutas y además fallan al compilar si alguien intenta usarlos desde el navegador.
- Las secciones del ERP se cargan desde una ruta común registrada en un único lugar, en vez de crear una página por pantalla.
Es el tipo de problema que no aparece en un tutorial y que solo se ve cuando el producto crece de verdad.
¿Cómo se mantiene ordenado un ERP que no para de crecer?
- Organización por áreas de negocio: facturación, reservas, amarres o clientes viven en carpetas propias con su lógica, sus pantallas y sus accesos a datos.
- Una ficha viva por módulo: qué hace, qué tablas usa y qué otros módulos dependen de él. Antes de tocar un módulo, se lee su ficha.
- Comprobaciones automáticas antes de desplegar: por ejemplo, que todas las tablas que usa el código existan en la base de datos.
- Pruebas reales en los módulos críticos: facturación, accesos y tesorería se prueban contra datos reales antes de cada cambio.
¿Qué haríamos igual y qué cambiaríamos?
Volveríamos a elegir PostgreSQL con seguridad por fila y la separación entre persona, acceso y relación con cada empresa: son las dos decisiones que más problemas nos han ahorrado. Lo que cambiaríamos es empezar antes con la disciplina de rutas y con las fichas por módulo; ordenar un sistema grande a posteriori cuesta mucho más que hacerlo desde el principio.
Si estás pensando en construir un producto parecido para tu sector, en nuestro servicio de desarrollo de software a medida te contamos cómo trabajamos, y aquí tienes el caso completo de Agora Marine.