M · A — Marco Avaria Caso 02 · Mundo Otaku

Mundo Otaku · Aplicación móvil full-stack

Leer el caso

Publicar, proponer,
conversar.

La aplicación permite publicar objetos de cultura otaku, proponer intercambios entre personas y conversar en tiempo real mientras se negocia. Mi proyecto de título de 2024, recuperado y modernizado en 2026.

Ver la app en uso

Diseño y desarrollo individual · Flutter + NestJS + PostgreSQL

Abrir el primer tomo Tomo 01 · Producto
Catálogo de Mundo Otaku en Android con productos y filtros en tema oscuro.
Catálogo de la app Android · flujo 03, 16 s del video aprobado.

Tomo 01 · Producto

Tipo

Proyecto de título individual

Ámbito

App móvil + API + base de datos + despliegue

Estado

Demo técnica · evidencia Android

Tecnologías

Flutter · Dart · NestJS · TypeScript · PostgreSQL · Docker

01 / El producto

Un intercambio no es una compra:
es una conversación.

Dos personas que quieren intercambiar objetos necesitan tres cosas seguidas: verlos, proponer el trato y poder hablar mientras lo cierran.

Mundo Otaku reúne ese recorrido en una sola aplicación: publicar un producto con su fotografía, encontrarlo en el catálogo de productos, proponer un intercambio a su dueño y abrir un chat en tiempo real para negociarlo. Lo diseñé y desarrollé como proyecto de título, de principio a fin y sin reparto de tareas con otras personas; en 2026 lo retomé para auditar el código heredado, corregir debilidades reales de seguridad y concurrencia, construir una suite de pruebas y desplegar su API en Render.

La explicación técnica se apoya en código y documentación del proyecto. Las capturas siguientes proceden de videos Android aprobados y muestran acciones concretas, con sus límites.

01

Publicar

Crear un producto con su descripción y su fotografía, tomada con la cámara o elegida de la galería.

Ver las pantallas
02

Proponer

Ofrecer un intercambio sobre el producto de otra persona y seguir su estado en listas de propuestos y recibidos.

Ver el recorrido
03

Conversar

Abrir un chat en tiempo real sobre WebSockets para acordar los detalles antes de cerrar el trato.

Ver el chat

Una primera mirada

La app en 48 segundos.

Un recorrido breve para ubicar el producto: acceso, catálogo, ficha, estante e historial. Después puedes seguir los capítulos para ver las acciones y sus decisiones técnicas.

Mi aporte

Diseñé y desarrollé el proyecto individual: interfaz Flutter, API NestJS y modelo de datos. En la recuperación de 2026 trabajé en autorización, consistencia de intercambios, pruebas, despliegue y modernización del cliente.

  1. Descubrir
  2. Publicar
  3. Proponer
  4. Responder
  5. Conversar
Ir directamente a ingeniería →
Recorrer la app 01 · 48 sAcceso, catálogo, ficha, estante e historial de chat. El historial anterior no prueba mensajes en vivo. Montaje editado, sin audio.
Leer el recorrido del video

La persona inicia sesión, recorre el catálogo, abre la ficha de Komi-san, consulta sus fotografías y visita Mi estante. Después entra en Chats y ve un historial existente.

Siguiente Tomo 02 · Descubrir

Tomo 02 · Descubrir

02 / El producto en uso

De encontrar algo a conversar sobre ello.

Cinco recorridos principales y tres complementarios. Las capturas muestran cada estado; los videos permiten seguir la acción. Todas las tomas provienen de la app Android en un teléfono físico.

01 / Recorrido principal

Explorar antes de proponer

El catálogo lleva de una búsqueda concreta a la ficha, las fotografías y la persona que publica. Ese contexto permite decidir qué producto interesa antes de iniciar un intercambio.

  • Mundo Otaku en Android: explorar en el recorrido «Encontrar un producto».
    01/03Explorar

    Catálogo de productos y filtros en la app Android. Flujo 03 · 16 s del video aprobado.

  • Mundo Otaku en Android: buscar en el recorrido «Encontrar un producto».
    02/03Buscar

    La búsqueda «Komi» deja visible un resultado; el teclado ocupa parte de la pantalla. Flujo 03 · 25 s del video aprobado.

  • Mundo Otaku en Android: abrir ficha en el recorrido «Encontrar un producto».
    03/03Abrir ficha

    Ficha de Komi-san con portada, autor de la publicación, etiquetas y descripción. Flujo 03 · 40 s del video aprobado.

  • Ver también la búsqueda en video · 35 s

    Buscar y abrir una ficha 03 · 35 sBúsqueda de Komi y recorrido de su ficha. El teclado forma parte de la toma. Montaje editado, sin audio.
    Leer el recorrido del video

    Se escribe Komi en la búsqueda, se abre el resultado y se examina la ficha. El teclado permanece visible durante parte del recorrido y el cierre dedica varios segundos al detalle.

Siguiente Tomo 03 · Publicar

Tomo 03 · Publicar

02 / Recorrido principal

De formulario a publicación

Publicar reúne validación, selección de una imagen y actualización del estante. La regla del tomo depende del tipo de producto: un manga exige un número y admite el tomo 0.

  • Mundo Otaku en Android: regla del formulario en el recorrido «Publicar y eliminar».
    01/06Regla del formulario

    El formulario pide revisar campos al intentar guardar sin tomo; parte del texto del error queda cortado en la interfaz. Flujo 06 · 35 s del video aprobado.

  • Mundo Otaku en Android: completar en el recorrido «Publicar y eliminar».
    02/06Completar

    Clasificación, descripción y etiquetas completas de Blue Lock antes de elegir la portada. Flujo 06 · 68.5 s del video aprobado.

  • Mundo Otaku en Android: portada lista en el recorrido «Publicar y eliminar».
    03/06Portada lista

    El formulario muestra la portada de Blue Lock antes de guardar el producto. Flujo 06 · 86 s del video aprobado.

  • Mundo Otaku en Android: resultado visible en el recorrido «Publicar y eliminar».
    04/06Resultado visible

    Blue Lock aparece en Mi estante, que en ese momento muestra cinco productos. Flujo 06 · 100 s del video aprobado.

  • Mundo Otaku en Android: eliminar en el recorrido «Publicar y eliminar».
    05/06Eliminar

    La app pide confirmar la eliminación de Blue Lock. Flujo 06 · 115 s del video aprobado.

  • Mundo Otaku en Android: volver al estante en el recorrido «Publicar y eliminar».
    06/06Volver al estante

    El estante vuelve a cuatro productos y Blue Lock ya no aparece en esta vista. Flujo 06 · 125 s del video aprobado.

  • Seguir la acción

    Publicar y eliminar 06 · 47 sBlue Lock pasa del formulario al estante y después se retira. La comprobación final de API y archivos pertenece al proyecto fuente. Montaje editado, sin audio.
    Leer el recorrido del video

    El formulario muestra los datos de Blue Lock y la portada elegida. La publicación aparece en Mi estante. Luego se solicita su eliminación y el estante vuelve a mostrarse sin ese producto.

Siguiente Tomo 04 · Proponer

Tomo 04 · Proponer

03 / Recorrido principal

Una propuesta que se puede retirar

La persona elige qué ofrece, confirma y puede seguir la propuesta en Enviadas. Cancelarla es una acción explícita; esta grabación muestra el recorrido completo de esa decisión.

  • Mundo Otaku en Android: elegir oferta en el recorrido «Proponer y cancelar».
    01/06Elegir oferta

    Desde Jujutsu Kaisen, el selector muestra Bleach entre los productos propios que se pueden ofrecer. Flujo 07 · 42 s del video aprobado.

  • Mundo Otaku en Android: confirmar en el recorrido «Proponer y cancelar».
    02/06Confirmar

    Diálogo de confirmación antes del envío. Flujo 07 · 48 s del video aprobado.

  • Mundo Otaku en Android: ver en enviadas en el recorrido «Proponer y cancelar».
    03/06Ver en Enviadas

    La lista Enviadas incluye la propuesta nueva junto a otra anterior. Flujo 07 · 70 s del video aprobado.

  • Mundo Otaku en Android: revisar detalle en el recorrido «Proponer y cancelar».
    04/06Revisar detalle

    Detalle de Bleach ofrecido por Jujutsu Kaisen, con la opción de cancelar. Flujo 07 · 76 s del video aprobado.

  • Mundo Otaku en Android: confirmar retiro en el recorrido «Proponer y cancelar».
    05/06Confirmar retiro

    La app pide confirmar la cancelación de esa propuesta. Flujo 07 · 80 s del video aprobado.

  • Mundo Otaku en Android: lista posterior en el recorrido «Proponer y cancelar».
    06/06Lista posterior

    La propuesta nueva ya no figura en la lista Enviadas visible. Flujo 07 · 90 s del video aprobado.

  • Seguir la acción

    Proponer y cancelar 07 · 42 sBleach se ofrece por Jujutsu Kaisen y luego se retira. No muestra a Demo 2 recibiendo esta propuesta. Montaje editado, sin audio.
    Leer el recorrido del video

    Se ofrece Bleach por Jujutsu Kaisen, se confirma la propuesta y se consulta en Enviadas. Después se solicita y confirma su cancelación.

Siguiente Tomo 05 · Responder

Tomo 05 · Responder

04 / Recorrido principal

Responder desde el otro lado

Recibidas permite revisar los dos productos antes de responder. Aquí se rechaza una oferta distinta de la anterior: primero se actualiza la lista, después se abre el detalle y se confirma el rechazo.

  • Mundo Otaku en Android: antes en el recorrido «Recibir y rechazar una propuesta nueva».
    01/05Antes

    Demo 2 ve solo una propuesta previa de Claymore en Recibidas. Flujo 09 · 26 s del video aprobado.

  • Mundo Otaku en Android: actualizar en el recorrido «Recibir y rechazar una propuesta nueva».
    02/05Actualizar

    Tras deslizar para actualizar, aparece además la oferta nueva de Hajime no Ippo. Flujo 09 · 29 s del video aprobado.

  • Mundo Otaku en Android: revisar en el recorrido «Recibir y rechazar una propuesta nueva».
    03/05Revisar

    Demo 2 entregaría Jujutsu Kaisen volumen 4 y recibiría Hajime no Ippo volumen 01. Flujo 09 · 38 s del video aprobado.

  • Mundo Otaku en Android: confirmar en el recorrido «Recibir y rechazar una propuesta nueva».
    04/05Confirmar

    El diálogo pide confirmar el rechazo de esa propuesta. Flujo 09 · 44 s del video aprobado.

  • Mundo Otaku en Android: resultado en el recorrido «Recibir y rechazar una propuesta nueva».
    05/05Resultado

    La tarjeta nueva desaparece y aparece «Se ha rechazado la solicitud»; la de Claymore permanece. Flujo 09 · 51 s del video aprobado.

  • Seguir la acción

    Recibir y rechazar 09 · 36 sUna propuesta nueva aparece tras actualizar Recibidas y luego se rechaza. Es otro intercambio; no continúa el flujo 07. Montaje editado, sin audio.
    Leer el recorrido del video

    Demo 2 actualiza Recibidas. Aparece una propuesta de Hajime no Ippo por Jujutsu Kaisen, se abre su detalle y se confirma el rechazo. La tarjeta desaparece de la lista y se muestra un aviso.

Siguiente Tomo 06 · Conversar

Tomo 06 · Conversar

05 / Recorrido principal

La conversación también tiene estado

El contador avisa de un mensaje cuando la conversación está cerrada. Al abrirla, la persona puede responder desde el teléfono y recibir una réplica sin salir del chat.

  • Mundo Otaku en Android: antes en el recorrido «Conversar durante un intercambio».
    01/05Antes

    Lista de Chats con el contador del intercambio de Komi-san en 0. Flujo 08 · 24 s del video aprobado.

  • Mundo Otaku en Android: aviso en el recorrido «Conversar durante un intercambio».
    02/05Aviso

    El contador pasa a 1 con la lista abierta; Demo 2 fue simulada por API y Socket.IO. Flujo 08 · 28 s del video aprobado.

  • Mundo Otaku en Android: leer en el recorrido «Conversar durante un intercambio».
    03/05Leer

    Chat abierto con un mensaje entrante; el historial conserva textos repetidos de un intento anterior. Flujo 08 · 32 s del video aprobado.

  • Mundo Otaku en Android: responder en el recorrido «Conversar durante un intercambio».
    04/05Responder

    Respuesta de Demo 1 visible bajo el historial anterior. Flujo 08 · 53 s del video aprobado.

  • Mundo Otaku en Android: réplica en el recorrido «Conversar durante un intercambio».
    05/05Réplica

    La respuesta de Demo 2 aparece al final mientras el chat sigue abierto. Flujo 08 · 58 s del video aprobado.

  • Seguir la acción

    Chat con respuesta 08 · 45 sContador 0→1, apertura del chat y respuestas con la pantalla abierta. Demo 2 fue simulada por API/Socket.IO; hay textos repetidos de una toma anterior. Montaje editado, sin audio.
    Leer el recorrido del video

    El contador pasa de 0 a 1 con la conversación cerrada. Demo 1 entra al chat, escribe desde el teléfono y recibe una réplica mientras la pantalla sigue abierta. Demo 2 se controla mediante API y Socket.IO; el historial incluye textos de una toma anterior.

Siguiente Tomo 07 · Más detalle

Tomo 07 · Más detalle

También importa lo que rodea al intercambio.

Validación, personalización y lectura de propuestas completan la experiencia. Conservo estos tres recorridos para mostrar el trabajo de interfaz con mayor profundidad.

06 / Más detalle

Orientar antes de enviar

Los formularios muestran qué corregir en los campos de acceso y registro. Estas capturas se concentran en la respuesta visible de la interfaz; las reglas del servidor se explican en Ingeniería.

  • Mundo Otaku en Android: acceso en el recorrido «Validar el acceso y el registro».
    01/02Acceso

    El formulario señala un correo sin formato y una contraseña demasiado corta. Flujo 05 · 10 s del video aprobado.

  • Mundo Otaku en Android: registro en el recorrido «Validar el acceso y el registro».
    02/02Registro

    El registro muestra errores de formato de correo, longitud y confirmación de contraseña antes de enviar. Flujo 05 · 68 s del video aprobado.

07 / Más detalle

Dar contexto incluso cuando no hay resultados

Un filtro sin productos, el cambio de tema y los estilos Capítulos y Webtoon muestran cómo se adapta la experiencia alrededor del recorrido principal.

  • Mundo Otaku en Android: filtrar en el recorrido «Ajustar la experiencia».
    01/03Filtrar

    El filtro Ropa presenta su estado vacío en el catálogo. Flujo 04 · 25 s del video aprobado.

  • Mundo Otaku en Android: antes en el recorrido «Ajustar la experiencia».
    02/03Antes

    Menú en tema claro con estilo «Capítulos» seleccionado. Flujo 04 · 37 s del video aprobado.

  • Mundo Otaku en Android: después en el recorrido «Ajustar la experiencia».
    03/03Después

    Menú en tema oscuro y estilo «Webtoon» seleccionado. Flujo 04 · 55 s del video aprobado.

08 / Más detalle

Examinar antes de decidir

Este recorrido complementario deja ver ambas caras de una propuesta que ya estaba pendiente. Sirve para estudiar su presentación, sin atribuirle una aceptación o un rechazo que no aparecen en la toma.

  • Mundo Otaku en Android: lista en el recorrido «Examinar una propuesta recibida preexistente».
    01/02Lista

    Demo 2 ve en Recibidas una propuesta ya existente. Flujo 02 · 28.5 s del video aprobado.

  • Mundo Otaku en Android: detalle en el recorrido «Examinar una propuesta recibida preexistente».
    02/02Detalle

    La ficha enseña lo que Demo 2 entregaría y recibiría, con opciones de aceptar o rechazar. Flujo 02 · 32 s del video aprobado.

Siguiente Tomo 08 · Ingeniería

Tomo 08 · Ingeniería

03 / El intercambio

Un objeto,
dos personas.

El modelo conecta cuatro niveles: la persona, el producto que publica, la propuesta de intercambio que otra persona le hace y la conversación que nace de esa propuesta. Es donde aparecen las reglas más interesantes del backend.

  1. Usuario Cuenta autenticada, dueña de lo que publica
  2. Producto Publicación con descripción e imagen
  3. Intercambio Propuesta entre dos usuarios, con estado
  4. Conversación Mensajes en tiempo real sobre esa propuesta
La decisión
Que la propiedad del recurso mande: solo quien publicó un producto puede modificarlo o responder una propuesta sobre él.
El detalle
Las transacciones y los bloqueos de escritura serializan cambios sobre un intercambio; una restricción única controla las propuestas activas duplicadas.
Qué muestran los videos
Los videos separan proponer y cancelar (07), examinar una propuesta anterior (02) y recibir y rechazar otra oferta (09). No hay un recorrido de aceptación grabado.

04 / La conversación

Hablar mientras
se negocia el trato.

El chat usa WebSockets con Socket.IO: el flujo 08 muestra una respuesta mientras la conversación está abierta. Del lado del servidor, un adaptador propio restringe desde qué orígenes se acepta una conexión, junto con la autenticación del canal.

  1. Conexión Socket.IO con origen restringido por adaptador
  2. Identidad La sesión JWT acompaña al canal
  3. Mensaje Se persiste y se reparte a la otra parte
  4. Historial La conversación sobrevive al cierre de la app
La decisión
Anclar la conversación al intercambio, no a un chat suelto: se habla de algo concreto.
El detalle
La API mantiene una lista de tokens revocados y valida el JWT al autenticar el socket. El cliente renueva la conexión al cambiar de sesión.
Qué falta medir
Comportamiento del chat con red intermitente y con muchas conversaciones abiertas a la vez.

05 / Una capa más abajo

Del gesto
al sistema.

Una propuesta de intercambio atraviesa la app, un contrato HTTP documentado, reglas de autorización y una base de datos relacional que tiene que decidir quién gana cuando dos personas actúan a la vez.

Explicación basada en el código local de la API. La aceptación y las carreras entre solicitudes no están demostradas en los videos de esta página.

INTERFAZFlutter / Riverpod / GoRouter
CONTRATO HTTP Y SOCKETNestJS / JWT / Swagger / Socket.IO
PERSISTENCIATypeORM / PostgreSQL

Ruta A / Camino limpio

Proponer y aceptar un intercambio.

  1. La app envía la propuesta con la sesión JWT de quien la hace.
  2. Un guard verifica la sesión y que el token no esté revocado; la validación declarativa revisa el cuerpo de la petición.
  3. El servidor comprueba que quien responde sea realmente la persona dueña del producto.
  4. El intercambio cambia de estado dentro de una transacción y la app refleja el resultado.

Ruta B / Condición de carrera

Dos respuestas al mismo intercambio.

  1. Dos peticiones llegan casi al mismo tiempo sobre el mismo intercambio.
  2. Un bloqueo transaccional deja que solo una avance a la vez sobre esa fila.
  3. Una restricción única de la base de datos impide propuestas activas duplicadas.
  4. La segunda petición recibe un error explícito en vez de sobrescribir en silencio.

Esta fue una de las correcciones concretas de la auditoría de 2026 sobre el código heredado, no una propiedad del proyecto original.

Diagrama basado en el código del proyecto. No ejecuta la aplicación ni realiza peticiones a su backend.

Recuperar y modernizar un proyecto existente

Este es mi proyecto de título de 2024. En septiembre de 2026 se recuperaron los datos y las imágenes, se revisaron reglas de acceso y concurrencia y se amplió la automatización de pruebas. El cliente pasó a Flutter 3.47.5 y Riverpod 3; el controlador de chat anterior basado en GetX se retiró. La modernización conserva el producto y permite seguir desarrollándolo sobre una base actualizada.

Organización del código, en la app y en la API

El cliente Flutter separa cada funcionalidad en tres capas —dominio, infraestructura y presentación— con estado reactivo en Riverpod, enrutamiento declarativo con GoRouter, cliente HTTP Dio y validación de formularios con Formz. El backend NestJS se organiza en módulos por dominio con inyección de dependencias y documenta su API REST con Swagger/OpenAPI.

Patrones de diseño que se usan de verdad

Repository para el acceso a datos con TypeORM; Adapter para restringir el origen de las conexiones de Socket.IO; Strategy para poder cambiar de proveedor de almacenamiento de imágenes sin tocar el resto del sistema. A eso se suman guards e interceptores propios de NestJS para autenticación y para el procesamiento de archivos subidos.

Autenticación: qué está implementado

Passport con JWT, contraseñas protegidas mediante hash con bcrypt, autorización basada en la propiedad del recurso, un mecanismo propio de revocación de tokens al cerrar sesión, almacenamiento seguro de credenciales en el dispositivo con flutter_secure_storage, y validación declarativa de la entrada con class-validator, class-transformer y Joi.

Del contenedor al despliegue

La API usa Docker y Docker Compose, con integración continua en GitHub Actions que ejecuta pruebas y análisis estático en los eventos configurados. El despliegue corre en Render con una base de datos PostgreSQL administrada en Neon, y las migraciones versionadas de TypeORM mantienen la integridad del esquema entre versiones.

Cómo se usó la IA en este proyecto

La aplicación no incorpora ninguna funcionalidad de inteligencia artificial: no hay dependencias de IA ni de aprendizaje automático en su backend ni en su cliente. Lo que sí hubo fue un uso estructurado de asistentes de programación durante la recuperación del proyecto, con un protocolo propio de traspaso de contexto entre sesiones y reglas explícitas —por ejemplo, no confirmar cambios en el repositorio sin mi aprobación. Las decisiones de arquitectura, alcance y estética, y la validación final, siempre fueron mías.

Cliente móvil

  • FLUTTER
  • DART
  • RIVERPOD
  • GOROUTER
  • DIO
  • FORMZ
  • SOCKET.IO
  • IMAGE_PICKER
  • SECURE STORAGE

API y datos

  • NESTJS
  • TYPESCRIPT
  • TYPEORM
  • POSTGRESQL
  • PASSPORT
  • JWT
  • BCRYPT
  • SWAGGER
  • CLASS-VALIDATOR
  • JOI

Calidad y despliegue

  • DOCKER
  • DOCKER COMPOSE
  • GITHUB ACTIONS
  • PLAYWRIGHT
  • JEST
  • FLUTTER TEST
  • RENDER
  • NEON
  • GIT
Siguiente Tomo 09 · Verificación

Tomo 09 · Verificación

06 / Calidad y alcance

Qué se verificó y cuándo.

Resultados registrados en el proyecto fuente, contrastados con su historial y documentación. Son verificaciones fechadas; no una ejecución nueva de las suites desde este portafolio.

ComprobaciónResultado registradoFecha y alcance
Cliente Flutter181 / 18127 sep 2026 · análisis sin hallazgos y APK debug construida.
API: pruebas unitarias52 / 5225 sep 2026 · reglas y servicios del backend.
API: pruebas E2E40 / 4025 sep 2026 · incluye normalización del correo en el acceso.
Recorridos web con Playwright8 / 825 sep 2026 · pruebas del cliente y la API locales.
Persistencia tras reinicio15 sep 2026Producto y dos fotos conservaron JSON y hashes tras reiniciar la API desplegada.

Las grabaciones Android son del 26 y 27 de septiembre de 2026, en un Galaxy SM-S938B con Android 16. Las esperas están editadas: no sirven para medir tiempos de respuesta. Las pruebas web y las grabaciones Android corresponden a verificaciones diferentes.

El alcance de este caso

Los videos muestran publicar, cancelar, rechazar y conversar; no muestran un intercambio aceptado de principio a fin ni una prueba de carga. El flujo 08 usa una contraparte simulada por API/Socket.IO y el 09 muestra refresco manual. Las decisiones de autorización, concurrencia y almacenamiento se sustentan en código y comprobaciones del proyecto.

Este proyecto permite conversar sobre desarrollo móvil, diseño de APIs, bases de datos relacionales, seguridad de aplicaciones, concurrencia, testing automatizado y despliegue en la nube: un recorrido completo por una misma pieza de software, hecha de principio a fin por una sola persona.