NICOLÁS BOTTARINI
YAM

YAM

Año:2019
Industria:
FoodTechStartup
Entregables:
androidioswebbackend
Tecnologías:
KotlinPostgreSQLJavalinjOOQReact NativeTypeScriptReactFirebase
Enfoque técnico:
Product DiscoveryTDDContinuous IntegrationDDDClean Architecture

Introducción

YAM era una startup de delivery que conectaba comensales con chefs cercanos. A diferencia de las apps de delivery de restaurantes, acá el plato lo cocinaba un chef y llegaba directo a tu casa.

Hicimos el producto completo, desde la idea: un discovery de producto con los fundadores, el diseño, dos apps mobile (una para comensales y otra para chefs), el backoffice y la API.

Pantallas
App de comensales (izquierda) y app de chefs (derecha)

Discovery de producto

Antes de escribir código hicimos un discovery junto a los fundadores: ocho sesiones de trabajo. El objetivo era entender el negocio, bajar la idea a un producto concreto y saber cuánto costaba construirlo.

Las actividades fueron:

  • Elevator pitch: cada participante escribió el suyo y los unificamos en uno, para acordar qué era el producto, para quién era y en qué se diferenciaba.
  • Definición de usuarios: armamos personas para los dos lados del negocio, los comensales (foodies) y los chefs (yammers).
  • Benchmark competitivo: analizamos a los competidores directos e indirectos, con sus fortalezas, debilidades y modelo de negocio.
  • Análisis de riesgos: identificamos los riesgos de adopción, de calidad y externos (legales, laborales, bromatología) y definimos acciones para mitigar cada uno.
  • Story mapping: mapeamos las tareas de cada actor (comensales, chefs y administradores) y separamos lo que entraba en el MVP de lo que quedaba para una segunda etapa.
Actividades del discovery
Elevator pitch, mapa de riesgos, story mapping y resumen de story points

Con el mapa definido pasamos al diseño: primero sketches en pizarrón de las pantallas principales y después wireframes navegables.

Del sketch al wireframe
Del sketch en pizarrón a los wireframes navegables
Del sketch al wireframe
Del sketch en pizarrón a los wireframes navegables

El discovery terminó con un product backlog de user stories estimadas en story points y organizadas en tres releases, una propuesta de equipo y una estimación de duración en dos escenarios de velocidad.

Qué hacía el producto

  • App de comensales. Registro con verificación por email, búsqueda de platos de chefs cercanos según la ubicación, detalle de cada plato con sus opciones y perfil del chef con su reputación (calidad, velocidad y packaging). El pedido se armaba en un carrito, con la dirección de entrega buscada en Google Places, y se pagaba con Mercado Pago.
  • App de chefs. El chef recibía una notificación push por cada pedido pago y lo confirmaba indicando en cuántos minutos iba a estar listo para retirar, o lo rechazaba con un motivo. Después lo marcaba como retirado.
  • Backoffice. Alta de chefs y de sus platos, con fotos, ubicación y publicación; listado de comensales; y seguimiento de los pedidos: asignar el cadete, cancelar y marcar como entregado.

Para el MVP no integramos un servicio de logística: los envíos los hacían cadetes propios, y el backoffice les mandaba los datos del pedido por WhatsApp.

Estados del pedido
Estados de un pedido y quién dispara cada transición
Estados del pedido
Estados de un pedido y quién dispara cada transición

Enfoque técnico

Lideré el proyecto técnicamente: definí la arquitectura, lideré al equipo y programé con ellos las partes más difíciles.

  • API: un monolito modular en Kotlin con Javalin y jOOQ sobre PostgreSQL, con Flyway para las migraciones. Estaba organizada en Bounded Contexts: identidad y accesos, membership (el registro de comensales y chefs), catálogo, pedidos y notificaciones. Cada contexto era un módulo de Gradle con su propio schema en la base, y usaba el modificador internal de Kotlin para que su dominio no se pudiera usar desde afuera del módulo.
  • Integración entre contextos: la mayoría eran llamadas síncronas a los casos de uso del otro contexto, a través de un adaptador en la infraestructura del que llamaba. Así, el dominio de cada contexto no conocía el modelo de los demás. Para las integraciones por eventos, los eventos vivían en un módulo separado del core del contexto que los publicaba, y los consumidores dependían solo de ese contrato.
Mapa de contextos
Mapa de contextos de la API
Mapa de contextos
Mapa de contextos de la API
  • Apps y backoffice: las tres con la misma arquitectura: casos de uso separados del dominio y de la infraestructura, y la lógica de vista en Presenters, clases de TypeScript puras. Las apps estaban hechas en React Native y el backoffice en React.
  • Pagos: integración con Mercado Pago.
  • Testing: aplicamos TDD en todo el producto. En la API, cada caso de uso tenía sus tests sociables, con repositorios en memoria y dobles solo para los servicios externos. En las apps y el backoffice testeábamos los casos de uso y los Presenters.
  • Monorepo: todo vivía en un solo repositorio, con Yarn workspaces para las apps y el backoffice, Gradle para la API y una consola propia para levantar la base, correr cada componente y ejecutar los tests y el linter. El pipeline de GitLab CI corría los tests y el linter de cada componente y desplegaba la API y el backoffice.