289 lines
13 KiB
HTML
289 lines
13 KiB
HTML
|
|
<!DOCTYPE html>
|
||
|
|
<html lang="es">
|
||
|
|
<head>
|
||
|
|
<meta charset="UTF-8">
|
||
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
|
|
<title>DoliMiddlewareApi — BFF para Dolibarr</title>
|
||
|
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/reveal.js@5.1.0/dist/reveal.css">
|
||
|
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/reveal.js@5.1.0/dist/theme/black.css">
|
||
|
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/reveal.js@5.1.0/plugin/highlight/monokai.css">
|
||
|
|
<style>
|
||
|
|
.reveal h1,.reveal h2,.reveal h3{text-transform:none;letter-spacing:0}
|
||
|
|
.reveal section{padding:22px 46px;box-sizing:border-box}
|
||
|
|
.reveal h1{font-size:1.7em;font-weight:800;margin-bottom:.1em}
|
||
|
|
.reveal h2{font-size:1.15em;margin-bottom:.5em;color:#fff;font-weight:700}
|
||
|
|
.reveal p{font-size:.8em;line-height:1.55;margin:.35em 0}
|
||
|
|
.reveal ul{font-size:.78em;line-height:1.7;margin:.2em 0 .2em 1.3em}
|
||
|
|
.reveal pre{width:100%;margin:.5em 0;border-radius:5px}
|
||
|
|
.reveal pre code{font-size:.62em;line-height:1.25}
|
||
|
|
|
||
|
|
body,.reveal{background:#111;color:#eee}
|
||
|
|
|
||
|
|
.cols{display:flex;gap:16px;align-items:stretch}
|
||
|
|
.cols>*{flex:1}
|
||
|
|
.center{text-align:center}
|
||
|
|
|
||
|
|
.card{
|
||
|
|
background:#181818;
|
||
|
|
border:1px solid #333;
|
||
|
|
border-radius:6px;
|
||
|
|
padding:16px 20px;
|
||
|
|
margin:5px 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
.callout{
|
||
|
|
background:#1c1a12;
|
||
|
|
border:1px solid #e2a84b44;
|
||
|
|
padding:14px 20px;
|
||
|
|
border-radius:6px;
|
||
|
|
margin:.7em 0;
|
||
|
|
}
|
||
|
|
.callout p{margin:0;font-size:.78em;line-height:1.5}
|
||
|
|
|
||
|
|
.badge{
|
||
|
|
display:inline-block;
|
||
|
|
background:#181818;
|
||
|
|
border:1px solid #444;
|
||
|
|
border-radius:3px;
|
||
|
|
padding:5px 12px;
|
||
|
|
font-size:.58em;
|
||
|
|
margin:2px;
|
||
|
|
color:#aaa;
|
||
|
|
font-family:'SF Mono',Consolas,monospace;
|
||
|
|
}
|
||
|
|
|
||
|
|
.flow{display:flex;align-items:center;justify-content:center;gap:16px;margin:1em 0;font-size:.85em}
|
||
|
|
.flow-box{padding:14px 26px;border-radius:6px;font-weight:700;text-align:center;font-size:.95em}
|
||
|
|
.flow-arrow{color:#e2a84b;font-size:1.5em;font-weight:800}
|
||
|
|
|
||
|
|
.img-full{width:100%;max-height:52vh;object-fit:contain;border-radius:6px;display:block;margin:0 auto}
|
||
|
|
|
||
|
|
.ep{display:grid;grid-template-columns:1fr 1fr;gap:6px 20px;margin-top:.4em}
|
||
|
|
.ep p{margin:0;line-height:1.65}
|
||
|
|
|
||
|
|
.sub{font-size:.68em;color:#888;line-height:1.5}
|
||
|
|
|
||
|
|
.glow{font-size:.95em;color:#e2a84b;font-weight:700}
|
||
|
|
.big{font-size:1.5em;font-weight:800}
|
||
|
|
</style>
|
||
|
|
</head>
|
||
|
|
<body>
|
||
|
|
<div class="reveal"><div class="slides">
|
||
|
|
|
||
|
|
<!-- PORTADA -->
|
||
|
|
<section class="center">
|
||
|
|
<p style="font-size:.65em;color:#888;margin-bottom:.5em">TFG — Javier Mengual — 2026</p>
|
||
|
|
<h1 style="color:#fff">DoliMiddlewareApi</h1>
|
||
|
|
<p style="font-size:.95em;color:#e2a84b;margin:.2em 0 1.8em">Tu frontend merece algo mejor que strings rotos</p>
|
||
|
|
</section>
|
||
|
|
|
||
|
|
<!-- EL PROBLEMA -->
|
||
|
|
<section>
|
||
|
|
<h2>El frontend no debería tener que lidiar con esto</h2>
|
||
|
|
<p>Dolibarr funciona. Pero su API devuelve un desastre que no puedes entregarle a un frontend moderno y decir "aquí tienes, apañatelas".</p>
|
||
|
|
<div class="cols" style="margin-top:.8em">
|
||
|
|
<div class="card" style="border-top:3px solid #f87171">
|
||
|
|
<p style="font-size:.8em;color:#f87171;font-weight:700;margin:0 0 8px">Lo que te da Dolibarr</p>
|
||
|
|
<div class="sub" style="line-height:1.85">
|
||
|
|
IDs como <code>"string"</code><br>
|
||
|
|
Fechas en timestamp Unix<br>
|
||
|
|
Estados que son números: <code>"1"</code><br>
|
||
|
|
snake_case inconsistente<br>
|
||
|
|
Credenciales volando por ahí
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
<div class="card" style="border-top:3px solid #4ade80">
|
||
|
|
<p style="font-size:.8em;color:#4ade80;font-weight:700;margin:0 0 8px">Lo que tu frontend merece</p>
|
||
|
|
<div class="sub" style="line-height:1.85">
|
||
|
|
IDs como <code>int</code><br>
|
||
|
|
Fechas ISO 8601<br>
|
||
|
|
Estados legibles: <code>"unpaid"</code><br>
|
||
|
|
camelCase<br>
|
||
|
|
Un JWT y ya está
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</section>
|
||
|
|
|
||
|
|
<!-- BFF -->
|
||
|
|
<section>
|
||
|
|
<h2>Un escudo entre tu app y el caos</h2>
|
||
|
|
<p>El patrón BFF —Backend for Frontend— no es un proxy. Es una capa que traduce, protege y enriquece. Tu frontend habla con él, y él se ocupa de lo demás.</p>
|
||
|
|
<div class="flow" style="margin:1.3em 0">
|
||
|
|
<div class="flow-box" style="background:#1e1510;color:#fb923c;border:2px solid #fb923c88">Dolibarr</div>
|
||
|
|
<span class="flow-arrow">⟶</span>
|
||
|
|
<div class="flow-box" style="background:#1a1710;color:#e2a84b;border:2px solid #e2a84b88">BFF</div>
|
||
|
|
<span class="flow-arrow">⟶</span>
|
||
|
|
<div class="flow-box" style="background:#0f1a11;color:#4ade80;border:2px solid #4ade8088">Tu frontend</div>
|
||
|
|
</div>
|
||
|
|
<ul>
|
||
|
|
<li>El frontend nunca habla con Dolibarr directamente</li>
|
||
|
|
<li>Si el ERP cambia algo, tocas un sitio, no cincuenta</li>
|
||
|
|
<li>Y puedes meterle cosas que Dolibarr jamás va a tener</li>
|
||
|
|
</ul>
|
||
|
|
</section>
|
||
|
|
|
||
|
|
<!-- ARQUITECTURA -->
|
||
|
|
<section class="center">
|
||
|
|
<h2>Así funciona</h2>
|
||
|
|
<img src="arquitectura.png" class="img-full" alt="Arquitectura" style="max-height:54vh">
|
||
|
|
</section>
|
||
|
|
|
||
|
|
<!-- .NET 10 -->
|
||
|
|
<section data-background-image="dotnetall.jpg" data-background-size="cover" data-background-position="center" data-background-opacity="0.12">
|
||
|
|
<h2>¿Por qué .NET 10?</h2>
|
||
|
|
<p>Si voy a poner una API en producción, quiero dormir tranquilo tres años.</p>
|
||
|
|
<img src="dotnetall.jpg" style="width:100%;max-height:32vh;object-fit:cover;object-position:center;border-radius:6px;display:block;margin-bottom:10px">
|
||
|
|
<div class="cols" style="gap:10px">
|
||
|
|
<div class="card" style="border-top:3px solid #e2a84b">
|
||
|
|
<p style="font-size:.76em;color:#e2a84b;font-weight:700;margin:0 0 3px">LTS hasta 2028</p>
|
||
|
|
<p class="sub">Tres años de soporte. Me da igual lo que pase meantime.</p>
|
||
|
|
</div>
|
||
|
|
<div class="card" style="border-top:3px solid #e2a84b">
|
||
|
|
<p style="font-size:.76em;color:#e2a84b;font-weight:700;margin:0 0 3px">Rendimiento</p>
|
||
|
|
<p class="sub">Lo suficientemente rápido para que la latencia no sea un problema.</p>
|
||
|
|
</div>
|
||
|
|
<div class="card" style="border-top:3px solid #e2a84b">
|
||
|
|
<p style="font-size:.76em;color:#e2a84b;font-weight:700;margin:0 0 3px">Todo incluido</p>
|
||
|
|
<p class="sub">JWT, rate limiting, health checks, OpenAPI. Sin buscar librerías raras.</p>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</section>
|
||
|
|
|
||
|
|
<!-- ENDPOINTS -->
|
||
|
|
<section>
|
||
|
|
<h2>Lo que cubre</h2>
|
||
|
|
<div class="ep">
|
||
|
|
<p><span style="color:#e2a84b;font-weight:600;font-size:.76em">Auth</span> <span class="sub">— login, JWT</span></p>
|
||
|
|
<p><span style="color:#e2a84b;font-weight:600;font-size:.76em">Facturas</span> <span class="sub">— CRUD, líneas, pagos, estado</span></p>
|
||
|
|
<p><span style="color:#e2a84b;font-weight:600;font-size:.76em">Clientes</span> <span class="sub">— con contactos incluidos</span></p>
|
||
|
|
<p><span style="color:#e2a84b;font-weight:600;font-size:.76em">Proveedor</span> <span class="sub">— facturas de proveedor</span></p>
|
||
|
|
<p><span style="color:#e2a84b;font-weight:600;font-size:.76em">Bancos</span> <span class="sub">— cuentas y movimientos</span></p>
|
||
|
|
<p><span style="color:#e2a84b;font-weight:600;font-size:.76em">Documentos</span> <span class="sub">— PDFs de facturas</span></p>
|
||
|
|
<p><span style="color:#e2a84b;font-weight:600;font-size:.76em">Setup</span> <span class="sub">— diccionarios, país, compañía</span></p>
|
||
|
|
<p><span style="color:#e2a84b;font-weight:600;font-size:.76em">Notificaciones</span> <span class="sub">— webhooks</span></p>
|
||
|
|
</div>
|
||
|
|
<div class="callout" style="margin-top:1em">
|
||
|
|
<p>Sí, gran parte es 1:1 con Dolibarr. Pero el BFF añade lo que el ERP no tiene: <strong>tipos correctos</strong>, <strong>estados legibles</strong>, enriquecimiento con nombres de cliente y notificaciones.</p>
|
||
|
|
</div>
|
||
|
|
</section>
|
||
|
|
|
||
|
|
<!-- TRANSFORMACIÓN -->
|
||
|
|
<section>
|
||
|
|
<h2>De francés y strings... a inglés y tipos</h2>
|
||
|
|
<div class="cols" style="align-items:flex-start;margin-top:.2em">
|
||
|
|
<div>
|
||
|
|
<p class="red" style="font-size:.76em;font-weight:700;margin-bottom:5px">Dolibarr</p>
|
||
|
|
<pre><code class="json" style="font-size:.62em">{
|
||
|
|
"statut": "1",
|
||
|
|
"total_ttc": "150.50",
|
||
|
|
"date": "1715673600",
|
||
|
|
"socid": "3"
|
||
|
|
}</code></pre>
|
||
|
|
</div>
|
||
|
|
<div>
|
||
|
|
<p class="green" style="font-size:.76em;font-weight:700;margin-bottom:5px">BFF</p>
|
||
|
|
<pre><code class="json" style="font-size:.62em">{
|
||
|
|
"status": "unpaid",
|
||
|
|
"total": 150.50,
|
||
|
|
"date": "2024-05-14",
|
||
|
|
"clientId": 3
|
||
|
|
}</code></pre>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
<p style="font-size:.78em;margin-top:.8em;color:#ccc">Mismo dato. Otro mundo.</p>
|
||
|
|
</section>
|
||
|
|
|
||
|
|
<!-- SWAGGER -->
|
||
|
|
<section class="center">
|
||
|
|
<h2>Documentación automática</h2>
|
||
|
|
<img src="swagger.png" class="img-full" alt="Swagger UI" style="max-height:50vh">
|
||
|
|
<p class="sub" style="margin-top:.5em">OpenAPI 3.1 generado al arrancar. Cada endpoint documentado.</p>
|
||
|
|
</section>
|
||
|
|
|
||
|
|
<!-- AUTENTICACIÓN -->
|
||
|
|
<section>
|
||
|
|
<h2>La contraseña nunca sale del servidor</h2>
|
||
|
|
<p>El frontend maneja un JWT. La API key de Dolibarr queda atrapada dentro del BFF. Si alguien intercepta el token del usuario, caduca en 8 horas y no tiene acceso al ERP.</p>
|
||
|
|
<div class="cols" style="margin-top:.8em">
|
||
|
|
<div class="card" style="border-top:3px solid #e2a84b">
|
||
|
|
<p style="font-size:.78em;color:#e2a84b;font-weight:700;margin:0 0 4px">1 — Login</p>
|
||
|
|
<p class="sub">El usuario se autentica. El BFF recibe la API key de Dolibarr y la guarda en memoria.</p>
|
||
|
|
</div>
|
||
|
|
<div class="card" style="border-top:3px solid #4ade80">
|
||
|
|
<p style="font-size:.78em;color:#4ade80;font-weight:700;margin:0 0 4px">2 — JWT</p>
|
||
|
|
<p class="sub">El BFF genera un JWT para el navegador. La API key nunca sale de ahí.</p>
|
||
|
|
</div>
|
||
|
|
<div class="card" style="border-top:3px solid #fb923c">
|
||
|
|
<p style="font-size:.78em;color:#fb923c;font-weight:700;margin:0 0 4px">3 — Cada petición</p>
|
||
|
|
<p class="sub">El JWT lleva un sessionId. Con ese sessionId se busca la API key en caché y se inyecta al vuelo.</p>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
<div class="callout" style="margin-top:.7em">
|
||
|
|
<p>Cada usuario tiene su propia API key aislada. Si se cachease en el HttpClient, todos compartirían la misma.</p>
|
||
|
|
</div>
|
||
|
|
</section>
|
||
|
|
|
||
|
|
<!-- NOTIFICACIONES -->
|
||
|
|
<section>
|
||
|
|
<h2>Lo que un wrapper puede hacer que el ERP no</h2>
|
||
|
|
<p>Al poner una capa por delante, puedes añadirle cosas que Dolibarr nunca va a tener. El ejemplo más claro: cuando una factura cambia de estado, el equipo se entera al momento. Sin abrir el sistema, sin mirar nada.</p>
|
||
|
|
<div class="cols" style="margin-top:.7em">
|
||
|
|
<div class="card" style="border-top:3px solid #c084fc">
|
||
|
|
<p style="font-size:.78em;color:#c084fc;font-weight:700;margin:0 0 4px">Teams, Slack, lo que sea</p>
|
||
|
|
<p class="sub">Configuras una URL y avisa donde quieras. Hoy es Teams, mañana puede ser Telegram, email, lo que haga falta.</p>
|
||
|
|
</div>
|
||
|
|
<div class="card" style="border-top:3px solid #4ade80">
|
||
|
|
<p style="font-size:.78em;color:#4ade80;font-weight:700;margin:0 0 4px">La interfaz ya está</p>
|
||
|
|
<p class="sub">Añadir un canal nuevo es implementar una interfaz. El resto del código ni se toca. Así se escala un wrapper de verdad.</p>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</section>
|
||
|
|
|
||
|
|
<!-- DESPLIEGUE -->
|
||
|
|
<section>
|
||
|
|
<h2>Un comando y funciona</h2>
|
||
|
|
<p class="sub" style="margin-bottom:.3em">docker compose up</p>
|
||
|
|
<pre><code class="yaml" style="font-size:.64em">services:
|
||
|
|
mysql:
|
||
|
|
image: mysql:8.0
|
||
|
|
environment:
|
||
|
|
MYSQL_DATABASE: dolibarr
|
||
|
|
volumes: [mysql_data:/var/lib/mysql]
|
||
|
|
|
||
|
|
dolibarr:
|
||
|
|
image: dolibarr/dolibarr:latest
|
||
|
|
depends_on: [mysql]
|
||
|
|
ports: ["80:80"]
|
||
|
|
|
||
|
|
bff:
|
||
|
|
build: ./DoliMiddlewareApi
|
||
|
|
depends_on: [dolibarr]
|
||
|
|
ports: ["5001:8080"]</code></pre>
|
||
|
|
<p style="font-size:.78em;margin-top:.7em;color:#bbb">MySQL, Dolibarr, el BFF. Tres contenedores, un comando.</p>
|
||
|
|
</section>
|
||
|
|
|
||
|
|
<!-- CIERRE -->
|
||
|
|
<section class="center">
|
||
|
|
<h1 style="font-size:2.2em">Preguntas</h1>
|
||
|
|
</section>
|
||
|
|
|
||
|
|
</div></div>
|
||
|
|
|
||
|
|
<script src="https://cdn.jsdelivr.net/npm/reveal.js@5.1.0/dist/reveal.js"></script>
|
||
|
|
<script src="https://cdn.jsdelivr.net/npm/reveal.js@5.1.0/plugin/highlight/highlight.js"></script>
|
||
|
|
<script>
|
||
|
|
Reveal.initialize({
|
||
|
|
hash: true,
|
||
|
|
slideNumber: 'c/t',
|
||
|
|
transition: 'fade',
|
||
|
|
transitionSpeed: 'fast',
|
||
|
|
plugins: [RevealHighlight],
|
||
|
|
width: 1100,
|
||
|
|
height: 680,
|
||
|
|
margin: 0.04,
|
||
|
|
center: false,
|
||
|
|
});
|
||
|
|
</script>
|
||
|
|
</body>
|
||
|
|
</html>
|