Case study
Liga Al Touch — Gestión de una liga amateur Liga Al Touch — Managing an amateur league
Una liga de fútbol amateur parece un CRUD hasta que aparecen las suspensiones, los comodines y el derecho de piso. La decisión que sostiene todo el sistema es cuál dato se deriva y cuál no. An amateur football league looks like a CRUD app until suspensions, wildcards and entry rules show up. The decision holding the whole system together is which data gets derived and which does not.
Contexto Context
Liga Al Touch organiza un torneo de fútbol amateur en Paraná. La tabla, los goleadores, el fair play, los cruces de playoffs y las sanciones del tribunal se llevaban a mano, repartidos entre planillas y grupos de WhatsApp. Me contrataron para hacer la plataforma: un sitio público donde los jugadores miran su equipo, y un panel donde la organización carga los partidos. Liga Al Touch runs an amateur football tournament in Paraná. Standings, top scorers, fair play, playoff brackets and disciplinary rulings were kept by hand, scattered across spreadsheets and WhatsApp groups. They hired me to build the platform: a public site where players check their team, and a panel where the organisers enter matches.
Problema Problem
El dominio es más complejo de lo que aparenta. Un partido no es solo un resultado: puede haber walkover, tarjetas que arrastran suspensiones a fechas futuras, un comodín que perdona una roja, ajustes disciplinarios que suman o restan puntos fuera de la cancha, y playoffs de Oro y Plata que se arman con los cruces según cómo terminó la fase regular. Si eso se modela mal, la tabla miente. Y en una liga, una tabla que miente es una discusión con la gente. The domain is more complex than it looks. A match is not just a score: there can be walkovers, cards that carry suspensions into future fixtures, a wildcard that forgives a red card, disciplinary adjustments that add or subtract points off the pitch, and Gold and Silver playoffs seeded from how the regular phase ended. Model that wrong and the table lies. And in a league, a table that lies is an argument with real people.
La decisión de fondo: qué se deriva y qué no The core decision: what gets derived and what does not
El sistema está partido en dos capas que nunca se mezclan. En una viven los hechos del partido —quién jugó, quién hizo los goles, qué tarjetas hubo— y de ahí se DERIVA todo lo calculable: tabla de posiciones, goleadores, fair play, valla menos vencida. En la otra vive el estado administrativo: suspensiones, comodines, ajustes disciplinarios. Eso NO se deriva: tiene su propia tabla y su propio ciclo de vida. La razón es que una decisión administrativa es un acto, no un cálculo. Si la derivás de los hechos, no la podés revertir, no sabés quién la tomó y no la podés auditar cuando alguien reclama. Separarlas hizo que agregar reglas nuevas —los playoffs de Oro y Plata llegaron después— no tocara nada de lo que ya andaba. The system is split into two layers that never mix. One holds match facts — who played, who scored, which cards were shown — and everything computable is DERIVED from there: standings, top scorers, fair play, clean sheets. The other holds administrative state: suspensions, wildcards, disciplinary adjustments. That is NOT derived: it has its own table and its own lifecycle. The reason is that an administrative decision is an act, not a calculation. Derive it from the facts and you cannot reverse it, you do not know who made it, and you cannot audit it when someone disputes it. Splitting them meant that adding new rules — the Gold and Silver playoffs came later — touched nothing that already worked.
Seguridad y datos personales Security and personal data
- Cada acción de servidor es, en los hechos, un endpoint público. Por eso la autorización es denegar por defecto en cada una, sin excepción. Es el error que más veces vi: confiar en que la pantalla que llama a la acción ya validó el rol.
- El archivo de proxy solo pone cabeceras y hace redirecciones. Jamás autoriza. Es la trampa clásica del framework: parece el lugar natural para poner el guardián de auth, y es el peor.
- Todo se valida en el servidor con esquemas que rechazan campos inesperados. El rol nunca se lee de lo que manda el front.
- El DNI de los jugadores no aparece en ninguna proyección pública, ni en logs, ni en nada cacheado. Las vistas públicas usan objetos de transferencia sin campos internos.
- Toda mutación deja registro de auditoría en la misma transacción, y ese registro no lleva datos personales.
- El caché público se invalida por etiqueta al guardar una ficha. Nada personal entra jamás en algo cacheable.
Lo que decidí NO hacer What I decided NOT to build
Nada de Redis, colas, microservicios ni separación de lecturas y escrituras. Es un monolito por capas y la escala es chica: una liga, unas decenas de equipos. Lo escribí como regla en el repo para no tentarme más adelante. La complejidad que no agregás es la única que seguro no te va a fallar un domingo a la noche antes de la fecha. No Redis, no queues, no microservices, no read/write separation. It is a layered monolith and the scale is small: one league, a few dozen teams. I wrote it down as a rule in the repo so I would not be tempted later. The complexity you do not add is the only kind guaranteed not to break on a Sunday night before matchday.
Mi rol My role
Freelance, todo: modelo de dominio, backend, sitio público, panel de administración y las rondas de devolución con la organización de la liga. Varias features salieron de esas devoluciones, no del plan original — la descarga de la tabla y de los cuadros como imagen, por ejemplo, porque así es como se comparten en los grupos. Freelance, everything: domain model, backend, public site, admin panel, and the feedback rounds with the league organisers. Several features came out of those rounds rather than the original plan — downloading the table and brackets as an image, for instance, because that is how they get shared in group chats.
Estado Status
Funcional y probado en local, con el fixture, los playoffs Oro y Plata, el tribunal y el panel completos. Todavía sin desplegar: sale con la próxima temporada de la liga, sobre mi propio servidor. Working and tested locally, with fixtures, Gold and Silver playoffs, the disciplinary panel and the admin all complete. Not deployed yet: it goes live with the league next season, on my own server.