<ndg> </ndg>
nonsensedevelopmentgroup.com
Side projects · arquitectura y proceso
[ndg] el nombre es una broma; el contenido no.

Software end-to-end, como side project.

Con una larga trayectoria en el sector IT, los últimos años los he pasado más en planificaciones y reuniones de producto que en el editor. Nonsense Development Group es mi laboratorio personal: cuatro apps que mantengo en paralelo a mi trabajo principal, publicadas en App Store y como APK directo, cada una con su backend y su infraestructura detrás. Algunas son medio absurdas a propósito; lo que me interesa de ellas es cómo están hechas. La idea, el diseño, la arquitectura, las tecnologías y el despliegue son míos; el código lo escribo en vibe coding con Claude Code, revisando y probando lo que genera. Una sola persona, todas las capas.

01
Análisis
Qué problema hay y para quién, antes de abrir el editor.
02
Arquitectura
Modelo de dominio y esquema de datos. En CheckBingo esa capa no sabe que existen Vue ni SQLite.
03
Desarrollo
Diseño de componentes y procesos. Codificación y despliegue.

Proyectos

Cuatro apps en producción. Cada una se eligió para practicar un patrón distinto: cliente-servidor con WebSocket, compartición con control de privacidad, offline-first y OCR en el propio dispositivo.

VoBo · Vocabulario Borde
vobo.es ↗
// juego de palabras en español · estilo Pasapalabra

Juego de vocabulario en castellano: cada carta plantea una regla de letra (empieza por, contiene o termina en) y el jugador responde con palabras válidas. Validación tolerante (acentos, espacios, mayúsculas) y modo multijugador en tiempo real. Ejercicio sobre arquitectura cliente-servidor con comunicación bidireccional: API REST para datos y WebSocket para partidas.

Frontend
Vue 3 Ionic 8 Capacitor 8 TypeScript strict Vite 5 vue-i18n Vitest Cypress
Backend
Go PostgreSQL Redis REST + WebSocket JWT passwordless
Infra
VPS Nginx systemd App Store APK directo
highlights
  • Pipeline de normalización determinista: cada respuesta atraviesa la cadena trim → collapse whitespace → lowercase → strip accents → strip puntuación. La Ñ se preserva como letra distinta del abecedario, no se normaliza a N. La integridad del mazo se valida en carga: regla, target_letter y valid_answers deben ser coherentes.
  • Una jugada = trabajo coordinado en BBDD: cada respuesta dispara registro del intento, actualización agregada de estadísticas del jugador autenticado y, en multijugador , emisión del evento a la sala. Las stats se acumulan jugada a jugada: un cierre forzoso de la app no pierde progreso.
  • Multijugador vía WebSocket: cada partida es una sala con canal bidireccional persistente. El servidor hace broadcasting de eventos (respuesta acertada, fallida u omitida, fin de turno) a todos los conectados. Redis sostiene presencia online y estado efímero de la sala; PostgreSQL guarda lo durable.
  • Backend Go con binario único: Gin + GORM + PostgreSQL + Redis + JWT HS256 passwordless. Compilación en local y scp al VPS, detrás de Nginx con systemd. Sin Docker en producción. La concurrencia de las salas se sostiene con goroutines.
  • Generación de código mediante vibe coding *
LoroParrot
loroparrot.com ↗
// síntesis de voz · listas compartibles

App de repetición por voz: el usuario crea un "loro", una lista de textos que la app reproduce con síntesis de voz. El modelo se articula en tres bloques (Inicio + Loro + Cierre): un texto opcional precede a cada elemento y otro lo cierra, generando combinaciones del tipo "Odio los Lunes por la mañana". Ejercicio sobre compartición de recursos con control de privacidad, autenticación passwordless por email y analítica de descargas.

Frontend
Vue 3 Capacitor 6 Vite 4 vue-i18n axios jose (JWT) native-audio
Backend
FastAPI Uvicorn SQLAlchemy PostgreSQL PyJWT Pydantic SMTP / email
Infra
VPS Nginx App Store APK directo
highlights
  • TTS con abstracción dual por plataforma: web e iOS usan Web Speech API nativa (SpeechSynthesisUtterance) con selección de voz del sistema; Android delega a un plugin Capacitor TTS nativo, porque dentro del WebView Android la API web falla. Arriba queda una sola interfaz, speak(text, options), con dos implementaciones detrás.
  • Multi-idioma a tres niveles independientes: la UI se traduce con vue-i18n (es/en), cada loro guarda su propio language en la tabla groups, y el TTS filtra las voces del dispositivo por ese código. Un loro francés se lee con voz francesa aunque la UI esté en castellano.
  • Compartición granular con tabla shared_with: cada loro tiene visibilidad public | private. Los privados se comparten escribiendo emails concretos en una tabla indexada; no hace falta que el destinatario tenga cuenta. El primer login con ese email recupera los loros recibidos. Borrado del padre arrastra texts, shared_with y downloads por cascade delete.
  • Backend FastAPI tipado de extremo a extremo: FastAPI + SQLAlchemy + Pydantic + PostgreSQL + PyJWT. Auth passwordless con código de 6 dígitos por SMTP propio; cada verification_request tiene expires_at y is_used para evitar reuso. La tabla downloads registra cada descarga; el contador agregado download_count vive en groups para queries sin joins.
  • Generación de código mediante vibe coding *
CumuloNimbo
cumulonimbo ↗
// acrónimos y términos más utilizados en cloud · AWS + Azure + GCP · 100% offline

App en castellano para manejar el vocabulario de AWS, Azure y Google Cloud: el que aparece en su documentación y el que preguntan sus certificaciones, incluidas las de IA y machine learning. El glosario es la entidad de primer nivel: 671 términos curados a mano, y el quiz es su capa de evaluación: 1.045 preguntas tipo examen, cada una enlazada al término que la explica. Ejercicio sobre arquitectura offline-first, modelado multi-cloud con tabla de equivalencias entre servicios (S3 ↔ Blob Storage ↔ Cloud Storage), y distribución dual en App Store y APK directo.

Frontend
Vue 3 Ionic 8 Capacitor 8 TypeScript Pinia Vite 5 Sass vue-i18n
Datos
SQLite (bundled) @capacitor-community/sqlite sql.js jeep-sqlite UUID PKs
Aplicación sin conexión
Sin backend Sin red Sin telemetría Sin tracking
highlights
  • Términos utilizados en certificaciones como: AWS CLF-C02 · SAA-C03 · AIF-C01 · MLA-C01 · Azure AZ-900 · GCP ACE.
  • 5 subtipos de pregunta: definición, acrónimos, casos de uso, diferenciación entre servicios y «entre nubes» (¿qué es S3 en Azure?).
  • SQLite empaquetado en el binario: la base viaja con la app y se copia al almacenamiento del dispositivo en el primer arranque vía copyFromAssets; a partir de ahí la app no hace ninguna petición de red.
  • Distribución dual: App Store para iOS, APK directo para Android. Sin tracking ni analítica de terceros.
  • Generación de código mediante vibe coding *
CheckBingo
checkbingo ↗
// ayudante de cartones de bingo · 3×9, 4×8 y 3×5 · OCR en el dispositivo · 100% offline

App que ayuda a no perder un cantar: el usuario fotografía sus cartones, la app los lee con el OCR del propio dispositivo y deja el resultado listo para rematarlo de un toque. Durante la partida marca los números cantados y detecta línea y bingo en todos los cartones a la vez. Ejercicio de IA aplicada: OCR nativo (Vision en iOS, ML Kit en Android) a través de un plugin Capacitor propio, y colocación en la rejilla con k-means 1D para las filas y el rango del propio número para la columna. No hay backend ni cuentas de usuario: todo ocurre en el teléfono.

Frontend
Vue 3 Ionic 8 Capacitor 8 TypeScript Pinia Vite 5 vue-i18n Vitest Playwright
Datos
SQLite @capacitor-community/sqlite offline-first arquitectura hexagonal
Visión y OCR
Vision (iOS) ML Kit (Android) plugin nativo propio k-means 1D
highlights
  • El OCR deja un borrador que el usuario corrige: el OCR nativo del sistema rellena entre un 73% y un 87% de las celdas y el resto se remata con un toque sobre la rejilla. La v1 descartó un pipeline completo con OpenCV tras diagnosticar, con una sonda instrumentada en un iPhone real, que su consumo de memoria (>1 GB) supera el límite del WebView de iOS.
  • Colocar cada número sin fiarse de la geometría: la fila sale de un k-means 1D sobre la coordenada Y; la columna, del propio número, porque cada columna tiene su rango (1-9, 10-19…). La posición horizontal solo sirve de comprobación. En la variante 3×5 densa, que no tiene rangos por columna, la columna vuelve al k-means con k = 5. El número de serie del cartón se descarta como outlier.
  • Dominio como lógica pura con arquitectura hexagonal: las reglas de cada variante (3×9 con bombo de 90, 4×8 con bombo de 80 y 3×5 densa: números por cartón, rangos por columna, detección de línea y bingo) no dependen de Vue, de SQLite ni del OCR. Más de 850 tests automáticos (Vitest + Playwright) y 12 ADRs documentando cada decisión.
  • Distribución dual verificable: App Store para iOS y APK directo para Android, con las huellas SHA-256 del fichero (CheckBingo_2.0.0.apk, 74.862.513 bytes) y del certificado de firma publicadas en la web.
  • Generación de código mediante vibe coding *

Contacto

Cuéntame en cuatro líneas qué tienes en mente. Respondo en uno o dos días.