Caso de estudio · producto propio
Pachanga Community: app y web de fútbol amateur, de principio a fin.
Organizar las pachangas de un grupo de fútbol, desde el calendario hasta las estadísticas. Móvil, web, enlaces universales, tiendas y despliegue: todo hecho y mantenido por una sola persona.
- Rol
- Arquitecto y desarrollador · producto completo
- Contexto
- Pachanga Community · producto propio
- Dominio
- Producto digital · fútbol amateur
- Tecnologías
- React NativeExpoTypeScriptSupabaseDrizzleGhostDockerTraefikDokployAtomic Design
Resumen ejecutivo
Pachanga Community organiza los partidos de fútbol de un grupo: calendario, equipos, estadísticas y comunidad en un solo sitio, en lugar de repartidos por un chat. La app está publicada en App Store y Play Store, y la web hace de escaparate, de host de enlaces universales y de canal de soporte.
Aquí no hay equipo detrás: hay producto, diseño, app, web, infraestructura y soporte en la misma persona. He picado poco código: el 95% lo ha escrito LaIA, que es como llamo al uso de LLM y agentes. Vengo siendo Tony Stark, delego tanto como puedo y aun así no doy abasto, porque son muchas las piezas que tienen que funcionar a la vez —infraestructura, cloud, web, stores, ingresos, monitoring, PostHog, etc.— para darle al usuario una experiencia cuidada, elegante, sencilla y directa que resuelva su problema. Todo va sobre optimistic update y sobre guiar al usuario, intuyendo la acción que quiere hacer.
Contexto
En un grupo de amigos que juega cada semana, la organización vive en un chat: quién juega, quién avisa a última hora, cómo se reparten los equipos y quién lleva la cuenta. Cuando el grupo crece, el chat deja de ser una herramienta y pasa a ser el problema.
El producto ataca eso: una app para el grupo, con privacidad como decisión de diseño. Los cambios se guardan en el móvil y se sincronizan con la nube; no hay perfiles públicos indexados ni venta de datos.
El problema
- Coordinar un grupo que cambia cada semana: convocados, ausencias y equipos equilibrados.
- Llevar estadísticas que la gente quiera mirar, sin convertirlo en una liga profesional.
- Funcionar con mala cobertura en un campo de fútbol, sin depender de la red para cada pantalla.
- Publicar en dos tiendas, en tres idiomas y sin equipo de soporte detrás.
Restricciones
- Una sola persona. Cada decisión de arquitectura tiene que reducir el coste de mantenerla seis meses después.
- Dos tiendas. App Store y Play Store con sus revisiones, sus builds y sus enlaces universales.
- Tres idiomas. Español, inglés y catalán, tanto en la app como en la web.
- Coste de infraestructura. Un despliegue propio barato y reproducible, no una plataforma gestionada por comodidad.
Mi rol
Todo. Arquitectura, dominio, app móvil, web, diseño de la identidad, despliegue, tiendas y soporte. Lo que sigue describe las decisiones, no un reparto de trabajo.
Arquitectura móvil: hexagonal sobre DDD
La app es React Native con Expo y TypeScript. La estructura es hexagonal sobre DDD: el flujo va de la pantalla a los hooks de dominio, de ahí a los casos de uso y de ahí a los puertos, que implementan adaptadores de Supabase o en memoria.
La capa de dominio es solo TypeScript, sin React Native, sin Expo y sin base de datos: los tipos y una línea de validación con Zod controlan el dato que entra, sale y se mueve dentro de la app. Los casos de uso se prueban sin infraestructura, y cambiar de proveedor es escribir otro adaptador. La interfaz se organiza en Atomic Design —átomos, moléculas, organismos, layouts y pantallas— para que un cambio visual no obligue a tocar la lógica.
En texto: la app y sus pantallas llaman a hooks de dominio; los hooks llaman a casos de uso; los casos de uso dependen de puertos; los adaptadores implementan esos puertos contra Supabase, memoria o caché. La interfaz se compone de particles, atoms, molecules, organisms, layouts y screens.
Un dominio que no es un CRUD
El modelo tiene equipo, temporada, partido y jugador, pero lo interesante está en los bordes. Un jugador pertenece a un equipo con su propio nombre y sus atributos; los partidos admiten jugadores fantasma que después reclaman su perfil con un código de un solo uso; las valoraciones entre compañeros calculan un nivel relativo dentro del grupo, no una puntuación absoluta.
Cada una de esas reglas tiene un caso de uso y sus pruebas. Es la parte del proyecto que más he reutilizado: cuando el dominio está aislado, la app puede cambiar de forma sin que el negocio se entere.
Arquitectura web: Ghost, tema generado y tres idiomas
La web es Ghost con un tema propio de Handlebars. El tema no se edita a mano: se genera desde fuentes propias —plantillas y ficheros de traducción— y el build escribe las variantes de cada idioma. Así, una página nueva en tres idiomas no significa tres plantillas que se desincronizan.
Además de escaparate, la web es el host de los enlaces universales y una superficie de descubrimiento: rutas de soporte, privacidad y términos por idioma, un `llms.txt`, un catálogo para agentes y `robots.txt` con señal de contenido.
Enlaces universales y fallback
Un enlace compartido por chat tiene que abrir la app si está instalada y llevar a la tienda si no lo está. Eso son dos piezas: `apple-app-site-association` para iOS y `assetlinks.json` para Android, servidos por un nginx propio; y un fallback en la web que redirige a la tienda cuando el enlace llega a un navegador móvil.
El caso incómodo es el enlace de reclamación de un jugador fantasma: si quien lo abre no tiene la app, no puede quedarse en una página de error. Por eso el 404 localizado también redirige a la tienda, en lugar de dejar al usuario en un callejón sin salida.
Despliegue y operación
En local, todo levanta con Docker Compose. En producción, la web corre en un stack con Traefik sobre Dokploy, con el contenedor de Ghost, la base de datos y el nginx de los enlaces universales. La app se compila y publica con EAS y Fastlane para las dos tiendas.
Nada de eso es glamuroso, pero es lo que hace que el producto exista: certificados, rutas, prioridades de Traefik, variables de entorno y una build que falla a la primera si un fichero de configuración no está donde toca.
Cómo se escribe el código: el agent-harness
Buena parte del código de Pachanga la escriben agentes. A ellos les toca implementar y probar; a mí, definir el problema, revisar y decidir. El harness es lo que hace que esa división funcione: ficheros .instructions que dicen cómo se escribe cada pieza, plugins de Biome que atajan lo que no debe ni llegar a revisión y scripts deterministas que comprueban lo que una revisión a ojo no ve.
No busco escribir más código más rápido. Busco que el agente escriba mejor: el mismo criterio en todas las capas, menos decisiones repetidas y más tiempo mío en lo que mueve el negocio. Cuando el patrón es el mismo de arriba abajo, revisar deja de ser arqueología.
Decisiones técnicas
- Hexagonal sobre DDD en el móvil. Dominio puro y adaptadores intercambiables, a cambio de más ficheros por funcionalidad.
- Local-first con Supabase. La app funciona sin red, a cambio de asumir sincronización y conflictos.
- Tema web generado. Una fuente para tres idiomas, a cambio de un paso de build que hay que entender.
- Infraestructura propia sobre Dokploy. Control y coste bajo, a cambio de operarla yo.
Resultados
- App publicada en App Store y Play Store, con su web y su canal de soporte.
- Un dominio probado sin infraestructura, con adaptadores para Supabase y para memoria.
- Tres idiomas en app y web desde una sola fuente de copia por página.
- Enlaces que abren la app cuando está instalada y llevan a la tienda cuando no.
Aprendizajes
- Llevar todo el producto cambia el criterio: cada pieza tiene que ser obvia para tu yo de dentro de seis meses.
- Me gusta hacer de lo complejo algo sencillo. Cuando todo el proyecto sigue el mismo patrón con reglas claras —enum → entidad → puerto → caso de uso, con los hooks inyectando el adaptador que implementa el puerto—, saber dónde vive cada pieza es rápido. En Pachanga he cambiado los adaptadores con cierta frecuencia, reimplementándolos para que pasaran los e2e, los unitarios y los mocks; sin esa arquitectura, cada cambio habría atravesado la app entera. Es inyección de dependencias, lo de backend, traído al frontend. Junto con los plugins de Biome y los scripts deterministas, es una de las piezas del agent-harness del proyecto.
- Los enlaces universales, las tiendas y el despliegue no los ve nadie, y son los que deciden si el producto está terminado o solo publicado.
Artículos relacionados
Siguiente paso
Si tienes un producto que mantener con un equipo pequeño, puedo ayudarte a decidir qué piezas conviene aislar para que el coste no crezca contigo.