Resumen
The Rollecito es la plataforma de pedidos detrás de una panadería de rollos de canela en Los Ángeles. No es una plantilla de tienda: opera el negocio. Los clientes navegan un menú por sucursal, configuran y pagan un pedido, y lo siguen con un código de rastreo, mientras la cocina trabaja esos mismos pedidos en una cola en vivo y el personal administra el catálogo, las promociones y las sucursales desde un back office aparte.
- Estados del ciclo del pedido
- 5CREATED → PAID → PREPARING → READY → COMPLETED, forzados en la base de datos
- Idiomas de la interfaz
- 2Inglés y español, intercambiables desde el encabezado
- Tipos de rol distintos
- 3Admin, manager y cliente, con inicio de sesión de personal aparte
Problema
Una panadería pequeña que vende un producto hecho al momento pierde dinero en dos lugares a la vez. Al frente, los pedidos llegan por llamadas y mensajes directos, se transcriben a mano y salen mal: una opción que falta, una cantidad mal escuchada, un cliente que llega antes de que algo esté horneado. Atrás, la cocina no tiene cola: tiene la nota que esté sobre el mostrador en ese momento.
El arreglo genérico es una página de pedidos de terceros. Resuelve el formulario y deja todo lo demás: sin vista de cocina, sin menú por sucursal, una comisión en cada pedido, y un catálogo que no puede expresar 'frosting de queso crema, agregar fresas, sin drizzle' sin convertir cada combinación en un producto aparte.
Restricciones
- Solo recolección en tienda, así que el sistema tiene que ser honesto con los tiempos en vez de prometer ventanas de entrega que no puede cumplir
- Primero móvil: la mayoría de clientes pide desde el teléfono, muchas veces mientras camina a la tienda
- El personal no es técnico — la vista de cocina tiene que ser usable durante el servicio sin capacitación
- Multisucursal desde el inicio, porque un menú que no está acotado a una sucursal está mal el día que abre la segunda
- Los pagos tienen que ser suficientemente confiables para ser la caja real del negocio, lo que implica confirmación por webhook y no confiar en el navegador
Rol y contribución
Creador del producto e Ingeniero de Software
- Diseñé el dominio de comercio: sucursales, catálogo, opciones, pedidos, pagos y promociones
- Construí la tienda para clientes, el flujo de checkout y la experiencia de seguimiento del pedido
- Construí la pantalla de cocina y el back office de personal, con una ruta de autenticación aparte
- Implementé los payment intents de Stripe y la confirmación del pedido por webhook
- Escribí el esquema MySQL, la capa de procedimientos almacenados y las herramientas de migración
- Monté el despliegue en contenedor y el pipeline de CI/CD con aprobación
Diseño del sistema
Arquitectura
Topología de comercio de The Rollecito
Dos clientes React — la tienda para clientes y el back office de cocina y administración — hablan con una sola API en Node y Express sobre HTTPS. Los clientes inician sesión con Firebase Authentication o piden como invitados; el personal se autentica aparte con una sesión JWT. La API mantiene conexiones Socket.IO abiertas para empujar cambios de estado del pedido tanto a la pantalla de seguimiento del cliente como a la cola de cocina. Lee y escribe MySQL exclusivamente a través de procedimientos almacenados, guarda las imágenes de producto en Amazon S3, y crea payment intents de Stripe cuya confirmación regresa como webhook.
- Tienda
- React
- Vite
- React Router
- Redux Toolkit
- i18n (inglés / español)
- Backend
- Node.js
- Express
- Socket.IO
- Helmet
- Google reCAPTCHA
- Datos
- MySQL 8
- Tercera forma normal
- Capa de datos con procedimientos almacenados
- Migraciones versionadas
- Identidad y pagos
- Firebase Authentication
- JWT para sesiones de personal
- Payment intents de Stripe
- Webhooks de Stripe
- Infraestructura
- Docker
- AWS Lightsail Container Service
- AWS Amplify Hosting
- MySQL administrado
- Amazon S3
- GitHub Actions con aprobación manual
Recorrido del cliente
El pedido empieza con una sucursal, no con un producto. Elegir el local acota todo lo que sigue: qué artículos existen, cuáles están disponibles hoy y dónde se recogerá el pedido.
De ahí el cliente configura un artículo mediante sus grupos de opciones, lo agrega al carrito y paga. El checkout no requiere cuenta — pedir como invitado es una ruta de primera clase, porque obligar a registrarse a un cliente nuevo que compra un pan de seis dólares es la forma en que una panadería pierde la venta.
Tras el pago el cliente recibe un código de rastreo. La pantalla de seguimiento no es un recibo estático: está suscrita a los mismos eventos de pedido que produce la cocina, así que el estado se mueve conforme la cocina avanza.
- Elegir sucursal
- Navegar el catálogo de esa sucursal
- Configurar las opciones del artículo
- Carrito y checkout, como invitado o con sesión
- Pagar con Stripe
- Seguir por código hasta que esté listo para recoger
Catálogo y modelo de dominio
El catálogo deliberadamente no es una lista plana de productos. Los artículos pertenecen a categorías, cargan grupos de opciones con sus propios valores, y se unen a sucursales, de modo que el mismo artículo puede existir en un local y no en otro. Los grupos de opciones se pueden clonar entre artículos, porque los toppings y frostings de una panadería se repiten en casi todo el menú y volver a capturarlos a mano es como un catálogo se desincroniza.
Los pedidos referencian el artículo configurado y sus opciones seleccionadas en vez de una cadena de producto precalculada, así que el total del pedido se puede recalcular del lado del servidor en cualquier momento y un pedido histórico se sigue explicando solo.
Ciclo de vida del pedido
Un pedido pasa por cinco estados — CREATED, PAID, PREPARING, READY, COMPLETED — y las transiciones se fuerzan en la base de datos y no en el código de aplicación. Un pedido no puede saltarse un estado ni ir hacia atrás.
Esa ubicación es intencional. El mismo pedido lo tocan la tienda, el manejador del webhook y la pantalla de cocina, a veces en el mismo segundo. Si la regla vive en uno de esos llamadores solo se cumple para ese llamador; en la base de datos se cumple para todos, incluido uno futuro que todavía no se ha escrito.
Operación de cocina
La vista de cocina es una cola en vivo por orden de llegada, no un dashboard. Los pedidos pagados aparecen sin recargar, el personal los avanza por preparación y listo, y un pedido se puede priorizar o cancelar cuando la realidad lo exige.
El mismo cambio de estado que redibuja la cola de cocina también se empuja a la pantalla de seguimiento del cliente. Un evento, dos audiencias — que es la razón por la que existe la capa de tiempo real.
Flujo administrativo
El personal inicia sesión por una ruta de autenticación distinta a la de los clientes, y el back office está organizado por rol: gestión de menú y opciones, sucursales, promociones, registros de clientes, historial de pedidos y reportes de ingreso.
El motor de promociones tiene un paso de vista previa, así que una regla de descuento se puede evaluar contra un pedido antes de publicarla — una salvaguarda barata contra una promoción que resulta más generosa de lo previsto una vez que se encuentra con un carrito real.
Despliegue y operación
El backend se publica como imagen Docker a un AWS Lightsail Container Service; la tienda se compila y se aloja en AWS Amplify. El despliegue lo dispara GitHub Actions al hacer merge a la rama de producción, detrás de una aprobación manual obligatoria — un freno deliberado sobre un sistema que recibe pagos reales.
La API está detrás de una línea base de cabeceras de seguridad y una lista estricta de orígenes permitidos, con reCAPTCHA en las rutas que de otro modo serían atractivas para abuso automatizado.
Monetización
El ingreso es directo: los clientes pagan su pedido con Stripe y la panadería se queda con la transacción en vez de una parte de ella después de la comisión de un marketplace. Ser dueño del checkout también significa ser dueño de la relación con el cliente, del catálogo y del precio — incluidas las promociones, que son parte de primera clase del sistema y no algo que se aplica a mano en el mostrador.
Decisiones clave y trade-offs
Decisión
Poner todo el acceso a datos detrás de procedimientos almacenados
- Contexto
- El sistema recibe pagos y lo mantiene un solo ingeniero. El estado del pedido lo escriben la tienda, un webhook de Stripe y la pantalla de cocina.
- Alternativas consideradas
- Un ORM con las reglas de negocio en la capa de servicio
- SQL escrito a mano en los controladores
- Enfoque elegido
- Un esquema MySQL en tercera forma normal donde el usuario de base de datos de la aplicación solo tiene privilegios EXECUTE, con toda lectura y escritura pasando por un procedimiento almacenado.
- Razón
- Pone los invariantes en la última capa antes de los datos. Un bug en un controlador, o un segundo llamador agregado después, no puede escribir un pedido a un estado imposible.
- Trade-off
- La lógica de negocio vive en SQL, que es más difícil de probar y versionar que el código de aplicación, y frena los cambios que tocan el esquema. Las migraciones y un script de reset existen específicamente para hacer ese costo llevadero.
- Resultado
- Las transiciones de estado del pedido quedan garantizadas en la capa de datos sin importar cuál de los tres llamadores esté escribiendo.
Decisión
Confirmar el pago desde el webhook de Stripe, no desde el navegador
- Contexto
- El navegador sabe cuándo un pago tuvo éxito, y usar esa señal es el camino más corto a un checkout funcional.
- Alternativa considerada
- Marcar el pedido como pagado en el callback de éxito del cliente
- Enfoque elegido
- El cliente crea un payment intent; el pedido pasa a `PAID` solo cuando el webhook de Stripe lo confirma, y los registros de pago se llevan de forma idempotente.
- Razón
- Un cliente que cierra la pestaña, pierde señal o envía dos veces no debe decidir si la panadería empieza a hornear. El webhook es la única fuente que es a la vez autoritativa y entregada de forma independiente del dispositivo del cliente.
- Trade-off
- Una ventana breve donde el cliente ya pagó y la pantalla todavía no se entera, más infraestructura de webhooks que asegurar y monitorear. El canal de tiempo real es lo que hace que esa ventana sea lo bastante corta para no importar.
Decisión
Acotar el catálogo a una sucursal desde la primera versión
- Contexto
- La panadería opera hoy una sola sucursal, y un menú global único habría sido más rápido de construir.
- Alternativa considerada
- Un menú global, con multisucursal agregada después cuando abra la segunda
- Enfoque elegido
- Las sucursales son una entidad de primera clase; los artículos se unen a sucursales y el cliente elige local antes de navegar.
- Razón
- Meter después una dimensión de sucursal a un catálogo vivo implica migrar pedidos, menús y disponibilidad que ya existen en producción. Construirlo mientras los datos son pocos cuesta un join.
- Trade-off
- Un paso extra al inicio del recorrido del cliente hoy, a cambio de no reescribir el flujo de pedidos el día que abra la segunda sucursal.
Decisión
Permitir checkout como invitado en vez de exigir cuenta
- Contexto
- Las cuentas facilitan el historial, las notificaciones y la recompra, y Firebase Authentication ya estaba integrado.
- Alternativa considerada
- Exigir registro antes del checkout
- Enfoque elegido
- Checkout de invitado como ruta de primera clase, con cuentas autenticadas disponibles para quien quiera historial.
- Razón
- La fricción de un registro se mide contra un solo pedido de bajo valor. La mayoría de clientes nuevos abandona en vez de registrarse.
- Trade-off
- Los pedidos de invitado tienen una identidad más débil, que es la razón por la que el seguimiento es por código y por la que el sistema no puede asumir un registro de cliente duradero en cada pedido.
Lo más difícil: un pedido, tres escritores
La misma fila de pedido la escriben tres actores independientes. El cliente la crea y puede reintentar. Stripe la confirma de forma asíncrona, posiblemente entregando el mismo webhook más de una vez. La cocina la avanza mientras las otras dos siguen en vuelo.
Cualquier par de ellos puede llegar en el orden equivocado o dos veces. Un webhook que aterriza después de que un miembro del personal ya empezó a preparar no debe regresar el pedido a PAID; una entrega duplicada no debe registrar el pago dos veces; un cliente que reintenta el checkout no debe crear un segundo pedido vivo para el mismo intent.
La respuesta fue dejar de tratar la corrección como responsabilidad de cada llamador. Los registros de pago son idempotentes, así que un webhook repetido no hace nada, y la máquina de estados la fuerza la base de datos, así que una escritura fuera de orden se rechaza en vez de ser meramente improbable. Ese es el razonamiento detrás de la capa de procedimientos almacenados: es el único lugar por el que los tres escritores necesariamente pasan.



Resultado
- Desplegado públicamente en app.therollecito.com y usado como el canal de pedidos de la panadería
- Los pedidos por teléfono y mensaje reemplazados por un flujo de autoservicio con totales calculados en el servidor
- La cocina trabaja desde una cola en vivo en lugar de notas escritas a mano, movida por los mismos eventos que ven los clientes
- Pagos cobrados directamente por Stripe, sin comisión de marketplace sobre el pedido
- Catálogo, precios y promociones administrados por el personal sin un desarrollador de por medio
Aprendizajes
- En comercio, el evento autoritativo es el que no viene del navegador del cliente. Diseñar primero alrededor del webhook evitó toda una clase de bugs en vez de arreglarlos después.
- Forzar la máquina de estados en la capa de datos se pagó sola en cuanto existió un tercer escritor. Las reglas en código de aplicación solo obligan al llamador que las recuerda.
- Multisucursal fue mucho más barato de construir antes de que hubiera datos en producción de lo que habría sido después.
- La pantalla de cocina fue la funcionalidad que cambió cómo opera el negocio; la tienda fue la parte que se veía.
Próximas mejoras
- Entrega a domicilio junto a la recolección, que agrega manejo de direcciones, despacho y un segundo modelo de tiempos
- Pruebas automatizadas sobre las rutas de pago y transición de estados, hoy el área con menos cobertura
- Monitoreo operativo estructurado y alertas sobre fallas de webhook
- Llevar la lógica de los procedimientos almacenados a la misma disciplina de revisión y pruebas que el código de aplicación
