NICOLÁS BOTTARINI
YAM

YAM

Year:2019
Industry:
FoodTechStartup
Deliverables:
androidioswebbackend
Technologies:
KotlinPostgreSQLJavalinjOOQReact NativeTypeScriptReactFirebase
Technical focus:
Product DiscoveryTDDContinuous IntegrationDDDClean Architecture

Introduction

YAM was a delivery startup that connected diners with nearby chefs. Unlike restaurant delivery apps, here the dish was cooked by a chef and went straight to your home.

We built the complete product, starting from the idea: a product discovery with the founders, the design, two mobile apps (one for diners and one for chefs), the backoffice and the API.

Screens
Diners app (left) and chefs app (right)

Product discovery

Before writing code we did a discovery together with the founders: eight working sessions. The goal was to understand the business, turn the idea into a concrete product and know how much it would cost to build.

The activities were:

  • Elevator pitch: each participant wrote one and we merged them into a single pitch, to agree on what the product was, who it was for and how it was different.
  • User definition: we created personas for both sides of the business, the diners (foodies) and the chefs (yammers).
  • Competitive benchmark: we analyzed direct and indirect competitors, with their strengths, weaknesses and business models.
  • Risk analysis: we identified adoption, quality and external risks (legal, labor, food safety) and defined actions to mitigate each one.
  • Story mapping: we mapped the tasks of each actor (diners, chefs and administrators) and separated what went into the MVP from what was left for a second stage.
Discovery activities
Elevator pitch, risk map, story mapping and story points summary

With the map defined we moved to design: first whiteboard sketches of the main screens and then navigable wireframes.

From sketch to wireframe
From the whiteboard sketch to the navigable wireframes
From sketch to wireframe
From the whiteboard sketch to the navigable wireframes

The discovery ended with a product backlog of user stories estimated in story points and organized into three releases, a team proposal and a duration estimate under two velocity scenarios.

What the product did

  • Diners app. Sign-up with email verification, search for dishes from nearby chefs based on location, the detail of each dish with its options, and the chef's profile with their reputation (quality, speed and packaging). The order was put together in a cart, with the delivery address searched in Google Places, and paid with Mercado Pago.
  • Chefs app. The chef got a push notification for each paid order and confirmed it by saying in how many minutes it would be ready for pickup, or rejected it with a reason. Then they marked it as picked up.
  • Backoffice. Creating chefs and their dishes, with photos, location and publishing; the list of diners; and order tracking: assigning the courier, cancelling and marking as delivered.

For the MVP we didn't integrate a logistics service: deliveries were made by our own couriers, and the backoffice sent them the order details through WhatsApp.

Order states
Order states and who triggers each transition
Order states
Order states and who triggers each transition

Technical approach

I led the project technically: I defined the architecture, led the team and coded the hardest parts with them.

  • API: a modular monolith in Kotlin with Javalin and jOOQ on PostgreSQL, with Flyway for migrations. It was organized into Bounded Contexts: identity and access, membership (the sign-up of diners and chefs), catalog, orders and notifications. Each context was a Gradle module with its own schema in the database, and used Kotlin's internal modifier so its domain couldn't be used from outside the module.
  • Integration between contexts: most of it was synchronous calls to the other context's use cases, through an adapter in the caller's infrastructure. That way, the domain of each context didn't know the model of the others. For event-based integrations, the events lived in a module separate from the core of the publishing context, and the consumers depended only on that contract.
Context map
Context map of the API
Context map
Context map of the API
  • Apps and backoffice: all three with the same architecture: use cases separated from the domain and the infrastructure, and the view logic in Presenters, pure TypeScript classes. The apps were built in React Native and the backoffice in React.
  • Payments: integration with Mercado Pago.
  • Testing: we applied TDD across the whole product. In the API, each use case had its sociable tests, with in-memory repositories and test doubles only for the external services. In the apps and the backoffice we tested the use cases and the Presenters.
  • Monorepo: everything lived in a single repository, with Yarn workspaces for the apps and the backoffice, Gradle for the API and our own console to start the database, run each component and run the tests and the linter. The GitLab CI pipeline ran the tests and the linter of each component and deployed the API and the backoffice.