Agua Viva
2024 — 2026Marketplace móvil de agua a domicilio en Venezuela que conecta clientes, tiendas y repartidores, sobre 6 microservicios. Publicada en Google Play.
En Venezuela, comprar agua potable suele significar llevar el botellón vacío a un centro de recarga, esperar y volver cargándolo, sin saber antes si la tienda está abierta o tiene agua. Agua Viva digitaliza ese proceso en una sola app con 4 roles: el cliente encuentra tiendas en el mapa, pide y sigue el pedido en vivo hasta su puerta; la tienda (dueño y encargados) gestiona pedidos, catálogo, tanques, repartidores y ventas de mostrador; y el repartidor recoge los vacíos, los lleva a la tienda y entrega los llenos siguiendo una ruta ordenada.
La app está hecha en React Native/Expo con Clean Architecture y el backend son 6 microservicios en NestJS y Laravel conectados por RabbitMQ, con una base PostgreSQL por servicio y tiempo real con Socket.IO. Hoy el cliente paga por pago móvil directo a la tienda y la plataforma registra las comisiones y liquidaciones entre plataforma, tienda y repartidor. La app está publicada en Google Play. Hice el diseño y desarrollo full stack: app móvil, API Gateway, microservicios, mensajería e infraestructura.
Precios que no se manipulan
La app muestra una estimación, pero el total lo cotiza el servidor (/orders/quote) y se envía expected_total; si el precio cambió mientras el cliente compraba, el backend rechaza el pedido.
NestJS y Laravel por RabbitMQ
Implementé en PHP el protocolo RPC de NestJS (reply_to + correlation_id) para que los dos frameworks se hablen directamente por la cola, sin adaptadores HTTP entre servicios.
Consistencia sin transacciones distribuidas
Una base PostgreSQL por servicio que solo comparte IDs; la consistencia se mantiene con holds de fondos y pasos compensatorios (saga ligera) orquestados en el gateway.
Tiempo real sin estado duplicado
Los eventos de Socket.IO invalidan o actualizan la caché de React Query en vez de guardar un estado paralelo, así el servidor sigue siendo la única fuente de verdad.
Mapa con muchos marcadores
Clustering con Supercluster, consultas por zona visible con debounce y keepPreviousData para que los marcadores no parpadeen al mover el mapa.
UX de reparto en lote
Selección múltiple reutilizable (una factory de stores Zustand con 3 instancias), acciones en lote y reordenamiento por drag & drop; la entrega final se confirma pedido por pedido porque libera el dinero.
App móvil
Un solo binario para 4 roles, en Clean Architecture.
Cuatro capas con dependencias en una sola dirección (domain, aplication, infrastructure, presentation) y la misma cadena en todos los módulos: action HTTP → mapper / caso de uso → hook de React Query → hook de pantalla → screen. Ninguna pantalla llama a axios ni tiene lógica de negocio. El inicio elige su dashboard según el rol con una tabla de despacho. Sesión con token en SecureStore y logout automático ante un 401; builds con EAS, versionado semántico y changelog automático.
Cliente
Tiendas en mapa, carrito, checkout y seguimiento en vivo.
Búsqueda de tiendas en 3 modos: mapa con clustering y consultas por zona visible, cercanas por GPS y favoritas. Carrito persistente de una sola tienda con pedido mínimo, precios escalonados por cantidad y tarifas de delivery escalonadas. El total lo cotiza el servidor y se valida con expected_total, así el precio no se puede manipular desde el cliente. Seguimiento del pedido con línea de tiempo de estados, calificaciones y ubicaciones de entrega elegidas en el mapa o pegando un link de Google Maps.
Tienda
Pedidos en vivo, catálogo, tanques, equipo y liquidaciones.
Panel de pedidos en vivo con filtros y pedidos programados: asignar repartidor, definir cantidades y cambiar el estado. Pedidos de clientes sin cuenta (online o de mostrador), catálogo con stock y price tiers, tanques con historial de recargas, modo de despacho configurable, resumen de ventas comparado con el período anterior y liquidaciones de comisiones con la plataforma y con cada repartidor. El dueño y el encargado tienen permisos distintos.
Repartidor
Cola de recogida, ruta en mapa y acciones en lote.
Cola de recogida con totales de litros, pedidos y tiendas, reordenable con drag & drop y visible en un mapa de ruta. En la ruta activa los pedidos se agrupan por la siguiente acción y se avanzan en lote con selección múltiple (assigned → going_to_pickup → picked_up → at_store → out_for_delivery); la entrega final se confirma pedido por pedido porque es la que libera el dinero. Incluye historial de entregas y cobros a cada tienda, con verificación de los pagos reportados.
API Gateway
Punto de entrada REST que orquesta los microservicios.
Recibe las peticiones de la app y las reparte por RPC sobre RabbitMQ. Mantiene la consistencia sin transacciones distribuidas, con holds de fondos y pasos compensatorios (saga ligera). En total el backend expone más de 200 endpoints REST y más de 200 message patterns RPC, y todo el entorno se levanta con docker compose up.
Realtime Service
Rooms por pantalla con permisos validados por el servicio dueño del dato.
Cada pantalla se suscribe a su room (mis pedidos, pedidos de la tienda, cola de recogida, ruta activa, un pedido concreto) y el servicio valida el permiso con el microservicio dueño del dato antes de aceptar. En la app, un cliente Socket.IO singleton sigue la sesión y el AppState, y los eventos invalidan la caché de React Query en vez de mantener un estado duplicado.
Pedidos
Máquinas de estado del pedido y del reparto.
Modela el ciclo de vida del pedido y del reparto con máquinas de estado, donde cada transición valida qué rol puede hacerla. Cotiza el total en el servidor y registra las comisiones de tienda y repartidor al completar cada entrega.
Tiendas
Tiendas, productos, tanques y búsqueda geoespacial.
Centraliza tiendas, productos, precios, tanques y equipo. La búsqueda geoespacial usa la fórmula de Haversine y consultas por bounding box en PostgreSQL, con Redis como caché. Para que Laravel hable con NestJS implementé en PHP el protocolo RPC de NestJS (reply_to + correlation_id) sobre RabbitMQ.
Auth y billetera
Autenticación OAuth2 y billetera digital.
Autenticación con Laravel Passport (OAuth2) y recuperación de contraseña con código de 6 dígitos. La billetera digital (saldo, recargas, transferencias con clave, holds de fondos y pagos QR) está construida y funcionando en backend y app, pero en pausa por una razón legal: centralizar los cobros requiere una figura jurídica constituida en Venezuela.
Notificaciones
WebSocket, push, email y WhatsApp con log por canal.
Consume los eventos de RabbitMQ y envía notificaciones por WebSocket, push (Expo + FCM), email y WhatsApp, guardando un log de entrega por canal. En la app hay una bandeja con marcado optimista como leída, y al tocar una notificación se abre la pantalla que corresponde según el rol.