Caso de trabajo · 2025 — 2026

Ocho meses construyendo el sistema que hace funcionar el salón de un restaurante.

Soy Ramiro Dondero, Mobile Engineer. Esta es la historia de Woki Partner: una app para Android, iOS y web que reemplaza el cuaderno de reservas, el plano de mesas en papel y las llamadas de confirmación por un solo sistema en tiempo real.

  • Flutter
  • Dart
  • BLoC
  • Clean Architecture
  • Firebase
Mockup de la app en tablet + mobile1400 × 1000

El producto

Qué es Woki Partner, en 30 segundos

Un restaurante lleno tiene, en cualquier momento, unas quince cosas pasando a la vez: mesas que se liberan, gente que llega sin reserva, alguien que pide cambiar de horario, un cumpleaños que necesita mesa junto a la ventana.

Woki Partner es la tablet que está en la recepción y resuelve todo eso. Muestra el plano del salón en vivo, quién llegó y quién no, la lista de espera, las solicitudes pendientes y la ficha de cada comensal. La misma app corre en el teléfono del encargado y en el navegador de la oficina.

Del otro lado hay un backend con el que trabajamos contrato a contrato, y una app de cliente donde el comensal reserva. Este caso es sobre el lado del restaurante.

Pantalla principal del guestcenter1200 × 800

Dimensión

Los números del proyecto

187K
líneas de Dart en 969 archivos
3
plataformas desde un solo codebase: Android, iOS y web
17
módulos de negocio en Clean Architecture
517
builds publicados — versión actual 4.4.10
1.092
commits y ~40 pull requests en 8 meses
936
claves traducidas en 3 locales (en, es-AR, es-CL)
−23.000
líneas netas eliminadas al migrar de streams de Firestore a REST, sin frenar el producto. 218 archivos tocados en una sola entrega.

El trabajo

Lo que construimos

Ocho entregas que cambiaron cómo se opera un salón. Cada una arrancó de un problema concreto que el restaurante ya tenía resuelto a mano.

01

Guestcenter: dos modos de visión

El problema Operar el día de hoy y planificar la semana que viene son dos trabajos distintos, pero vivían en la misma pantalla. El recepcionista se perdía entre las mesas de esta noche y los cupos del sábado.

Lo que hicimos Partimos la app en dos modos. Visión Recepcionista muestra el día en curso con el estado real de cada mesa, minuto a minuto. Visión Agenda muestra la planificación por turnos, para cargar y mover reservas futuras. El mismo dato, dos lecturas, sin duplicar lógica.

Los dos modos, lado a lado1200 × 800
02

Mesas: asignar, arrastrar, intercambiar

El problema Asignar una mesa era abrir un formulario, elegir de una lista y guardar. En el salón, con gente parada en la puerta, eso es demasiado lento.

Lo que hicimos Drag & drop sobre el plano del salón. Arrastrás la reserva a una mesa y queda asignada; la soltás encima de otra reserva y se intercambian. Selección múltiple para juntar mesas, desasignar con un gesto, y detección de conflictos entre subturnos antes de confirmar el movimiento.

Drag & drop sobre el plano del salón1200 × 800
03

Walk-ins y lista de espera

El problema El que llega sin reserva es una parte enorme de la noche y no existía en el sistema. Se anotaba en un papel al costado de la tablet.

Lo que hicimos Walk-in con sentada directa en un paso, y una lista de espera con tiempo estimado que se convierte en reserva sola en cuanto se libera una mesa que sirve. Con su propio historial, para poder mirar después cuánta gente se fue sin esperar.

Walk-in y cola de espera1200 × 800
04

Solicitudes

El problema Las reservas que llegan de la web y necesitan aprobación, más los pedidos de cambiar horario o cantidad de comensales, se mezclaban con todo lo demás y se contestaban tarde.

Lo que hicimos Una bandeja propia, separada del día operativo. Aprobar, rechazar o proponer una alternativa, con el historial completo de cada pedido y notificación push cuando entra uno nuevo.

Bandeja de solicitudes1200 × 800
05

Cobros: depósitos, garantías y gift cards

El problema El no-show tiene un costo real — mesa vacía en la mejor franja de la noche — y el restaurante no tenía ninguna herramienta para cubrirlo.

Lo que hicimos Depósitos y garantías con tarjeta, cobrables por link que se le manda al comensal. Soporte de cobros internacionales y validación de teléfonos país por país. Y gift cards que se emiten, se controlan y se canjean contra la reserva.

Depósito, garantía y gift card1200 × 800
06

La ficha del comensal

El problema Un restaurante sabe cosas de sus clientes que ningún sistema guardaba: el que siempre pide la mesa del fondo, el celíaco, el que viene cada jueves hace seis años.

Lo que hicimos Clientes propios del partner, con historial de visitas y un sistema de señales: automáticas, extraídas de lo que el comensal ya declaró al reservar, y manuales, de un catálogo que arma el propio restaurante. El orden y el color de cada señal los define el backend, así que se pueden cambiar sin tocar la app.

Ficha del comensal con señales1200 × 800
07

Brief pre-turno y control de disponibilidad

El problema Lo que hay que saber antes de abrir — entran 40 personas de un evento, falta un mozo, se acabó el pescado del día — se transmitía de boca en boca y le llegaba a la mitad del equipo.

Lo que hicimos Una nota de turno que se publica y que todo el equipo ve al entrar a la app. Al lado, un control para abrir o cerrar cupos en vivo, con motivo, cuando la cocina no da abasto o se cae un turno entero.

Brief del turno y control de cupos1200 × 800
08

Experiencias

El problema Un menú degustación, una cata, una cena de fin de año: productos que se venden por cupo y no encajaban en el modelo de "una reserva es una mesa a una hora".

Lo que hicimos Experiencias vinculadas a la reserva, y experiencias no vinculadas con su propia disponibilidad y ocupación independiente del salón. Cupos controlados por turno, y una capa de coordinación para que ambos tipos convivan en la misma agenda sin pisarse.

Experiencias y control de cupos1200 × 800

Sobre mí en este proyecto

Mi rol

Todo lo de arriba lo construimos en equipo. Esto es lo que puse yo.

Features de punta a punta

Del modelado del dominio a la UI en las tres plataformas: entidades, casos de uso, repositorios, BLoCs y vistas. Sin tirar la pelota a otro en el medio.

Arquitectura y convenciones

Sostener la separación por capas a medida que el repo pasaba de 5 a 17 módulos, y dejar escritas las reglas que hacen que el código nuevo se parezca al que ya está.

Contratos de API

Diseño de los endpoints junto al equipo de backend: qué campos, qué estados y de qué lado vive cada decisión. Buena parte de la complejidad se resuelve o se hereda acá.

Releases

517 builds a Android, iOS y web. Flavors por entorno, builds ofuscados con los símbolos archivados, distribución a testers y despliegue de la web.

Producción

Seguimiento de crashes y performance sobre tablets reales en salones reales, y la disciplina de deobfuscar y reproducir antes de tocar una línea.

Code review

Revisión de PRs del equipo, con foco en que el patrón se mantenga: nada de lógica en la UI, errores tipados, y widgets en vez de métodos que devuelven widgets.

Bajo el capó

Cómo está construido

Clean Architecture por feature. Cada módulo tiene sus cuatro capas y la dependencia apunta siempre hacia adentro: la UI conoce al dominio, el dominio no sabe que existe Flutter.

Diagrama de las cuatro capas de la arquitectura presentation Vistas desktop y mobile · widgets · BLoCs de UI Flutter application Casos de uso (una clase = una acción) · BLoCs de negocio flutter_bloc · dartz domain Entidades · enums de estado · errores tipados · interfaces Dart puro infrastructure Implementación de repositorios · HTTP · almacenamiento local Dio · Firebase

Stack

Flutter · Dart
Android, iOS y web desde un solo codebase
flutter_bloc
Estados explícitos por flujo, nunca booleanos sueltos
dartz
Either<Failure, T> en toda la capa de aplicación
get_it
Inyección de dependencias, registro por feature
auto_route
Ruteo con guards y tabs anidados, URLs reales en web
Dio
HTTP con interceptores de caché y de performance
Firebase
Auth, Messaging, Remote Config, Crashlytics, Performance, Analytics
Sentry
Segunda red de captura de errores en producción

Criterio

Tres decisiones que valieron la pena

01

Sacar Firestore de la ecuación

Los streams de Firestore daban tiempo real sin escribir código, y por eso el producto arrancó ahí. El costo apareció después: un modelo de datos duplicado entre Firestore y la API, una factura que crecía con cada tablet abierta en cada salón, y una capa de sincronización que ya nadie podía razonar entera.

Migramos a REST con actualización dirigida: pedimos datos cuando hay motivo para pedirlos, no permanentemente. Una sola fuente de verdad, costo predecible y un flujo que se lee de arriba a abajo.

218 archivos · +6.112 / −29.574 líneas · sin ventana de mantenimiento

02

Un codebase, tres plataformas, dos layouts

La tentación con Flutter es hacer una sola UI responsive y darla por buena. No sirve acá: el plano del salón en una tablet de 12" y la lista de reservas en un teléfono no son la misma pantalla más chica, son dos herramientas distintas para dos momentos distintos.

Separamos las vistas en desktop/ y mobile/ donde la experiencia diverge de verdad, y compartimos el 100% del dominio, los casos de uso y los BLoCs. Cada plataforma se ve como corresponde y ninguna regla de negocio se escribe dos veces.

Android · iOS · Web — mismo repo, mismos releases

03

Errores que no se pueden ignorar

Toda la capa de aplicación devuelve Either<Failure, T>. No es preferencia estética: hace que olvidarse de manejar un error sea un error de compilación y no un bug que aparece un viernes a las nueve de la noche con el salón lleno.

Encima de eso, Crashlytics y Sentry en paralelo, traces de performance en los flujos críticos, y builds de release ofuscados con los símbolos archivados por versión — así cualquier stacktrace de producción se puede leer meses después.

Fallos tipados · doble captura en producción · stacktraces legibles

Hablemos

¿Charlamos?

Si te interesa lo que leíste y estás buscando alguien que construya producto de punta a punta, escribime.