Architect a Ride-Hailing Platform

Design a production-ready ride-hailing system with C4 diagrams and ADRs.

Introduction

30 Second Summary

Every time you open a ride-hailing app, your request bounces between a dozen services in under 15 seconds. Behind that seamless experience is an architecture that someone had to design before a single line of code was written.

In this project, you will design the complete solution architecture for a real-time ride-hailing platform using Miro. You will produce the exact deliverable a solutions architect presents to a CTO or engineering team before development begins.

What You'll Build

You'll walk a stakeholder through your Miro board, showing them exactly how riders, drivers, and external systems interact, why each microservice exists, and what happens at every step of a ride from request to payment.

By the end of this project, you'll have:

  • A System Context diagram you can present to a stakeholder, showing exactly which external systems your platform depends on and what each integration does.
  • A Container diagram that proves why a monolith fails for this problem and justifies each microservice with real technical constraints you discovered yourself.
  • Complete Data Flow, Entity Relationship, and Architecture Decision Record artifacts that let you walk an interviewer through runtime behavior, data relationships, and trade-off analysis for every technology choice.
  • Secret Mission: A Non-Functional Requirements matrix with specific SLA targets and throughput estimates that prove you think about systems under real-world production load.

Are there any prerequisites?

You need a Miro account with the Business plan (or the free 14-day Business trial) to access the AWS shape packs used in this project. The Business plan costs $25 per month billed monthly, or $20 per month billed annually.

No coding experience is required. Familiarity with basic diagramming tools (Miro, Figma, or similar) is helpful but not essential.

Before We Start

Before We Start...

Before we open any tools, let's lock in what you're about to build and why it matters.

A ride-hailing platform is not a simple CRUD app. It juggles real-time GPS tracking, instant driver matching, financial transactions, and millions of concurrent users. That complexity demands deliberate architectural planning before a single line of code gets written.

Set Up Your Miro Workspace

Architecture diagrams without structure become a disorganized mess that no stakeholder can follow. When you present to a CTO or engineering team, they need to find the right diagram instantly.

In this step, you will set up your Miro workspace with the professional tools and structure needed for architecture documentation. You will activate the AWS shape pack and create five dedicated frames for each type of diagram you will build.

In this step, get ready to:
  • Sign in to Miro and activate the Business plan (or 14-day free trial).
  • Create your architecture board and enable the AWS shape pack.
  • Set up five frames to organize your diagrams.
Create a Miro account and start the Business plan free trial

The Business plan unlocks advanced shape packs (AWS, Azure, Kubernetes) that you need for professional architecture diagrams. The Free plan has basic diagramming but not these cloud-specific icon libraries.

  • Go to miro.com and sign up for a new account, or log in to your existing account.
  • Click Explore Business or navigate to the pricing page to find the Business plan.
  • Click the button to start the free 14-day Business trial.

What does the Business plan cost?

After the 14-day free trial, the Business plan costs $25 per member per month (billed monthly) or $20 per member per month (billed annually).

You can downgrade back to the Free plan before the trial ends to avoid charges.

You should now see the Miro dashboard with Business plan features active.

Create your architecture board and enable shape packs

Each architecture document lives on its own board. You will create a dedicated board and then activate the AWS icon library so you have official service icons available.

  • On your Miro dashboard, click + Create new.
  • Click Board from the menu that appears.
  • Click the board name at the top-left of the board to rename it.
  • Type RideFlow Platform - Solution Architecture and press Enter.

Now enable the AWS shape pack so you have official cloud service icons available.

  • Click the Diagramming icon in the creation toolbar on the left side of your board.
  • Click More Shapes at the bottom of the Diagramming shapes panel.
  • Under Miro Shapes, check the box next to AWS to activate the AWS shape pack.
  • Close the shape library modal.

You should now see AWS service icons (compute, database, networking categories) available in your Diagramming shapes panel.

Don't see the Diagramming icon?

The Diagramming icon looks like a flowchart shape in the creation toolbar on the left edge of your board. If your toolbar is collapsed, hover over the left edge to expand it.

If you still don't see it, confirm your Business trial is active by checking for the plan badge in your account settings.

Still stuck? Help me find the Diagramming icon and AWS shapes in Miro

Set up the board structure with frames

Professional architecture documents organize each diagram into its own section. In Miro, frames serve as containers that group related content and make navigation easy.

  • Click the Frame icon in the creation toolbar on the left side of your board (or press F on your keyboard).
  • Click and drag on the board to create a large frame.
  • Double-click the frame title area and type System Context (C4 Level 1) to name it.

Repeat this process to create four more frames arranged to the right of the first one.

  • Create a second frame and name it Container Diagram (C4 Level 2).
  • Create a third frame and name it Data Flow - Ride Lifecycle.
  • Create a fourth frame and name it Entity Relationship Diagram.
  • Create a fifth frame and name it Architecture Decision Records.

Why frames instead of just drawing anywhere?

Frames keep each diagram self-contained. You can export individual frames as images, present them as slides, and share links that jump directly to a specific diagram.

This structure mirrors how professional architects organize their deliverables. Each frame will hold one complete diagram type.

Before you move on, what do you expect to see when you zoom out to view the entire board?

  • Use the zoom controls in the bottom-right corner to zoom out until all five frames are visible on screen.

You should see five clearly labeled frames arranged left-to-right on your board. The AWS shape pack should appear in your Diagramming shapes panel when you click the Diagramming icon.

Frames overlapping or not visible?

If your frames are stacked on top of each other, click and drag each frame to reposition them left-to-right. Make sure each frame is large enough to hold a diagram (roughly the size of a laptop screen).

If you only see four frames, click the navigation panel in the bottom-left corner of your board. All five frames should be listed there.

Need help arranging your frames? Help me arrange my five architecture frames in Miro

Your workspace is ready. Next up, you will fill the first frame with a System Context diagram that maps out everything your ride-hailing platform touches.

Build the System Context Diagram

Your workspace is set up with five organized frames ready to fill. Now it's time to tackle the first and most important diagram in any architecture document: the System Context diagram.

Before you can design the internals of a system, you need to establish clear boundaries. What is your platform responsible for? What belongs to external services? Without these boundaries, teams build features that should live in external systems or miss critical integrations entirely.

In this step, get ready to:
  • Place the RideFlow Platform as the central system and define four external actor roles around it.
  • Add six external software systems with labeled, directional relationship arrows.
  • Complete the diagram with a notation legend and title following C4 conventions.
Identify and place external actors and the system boundary

A C4 Model System Context diagram shows your entire system as a single box in the centre, surrounded by the people who use it and the other systems it depends on. Every element needs a name and a short description. This convention makes diagrams self-documenting.

  • Double-click inside Frame 1 (labeled "System Context (C4 Level 1)") to zoom into it.
  • Press R on your keyboard to select the rectangle tool.
  • Draw a large rounded rectangle in the centre of the frame.
  • Double-click the rectangle and type RideFlow Platform as the title.
  • Below the title, add the description: Real-time ride-hailing system handling matching, tracking, and payments.

You should now see a single solid rectangle in the centre of Frame 1 with the platform name and description inside it. This box represents your entire system boundary.

  • Now add four Person elements around the central box. Use simple shapes (ovals or small rectangles with a person label) to represent each role.
  • Place "Rider" to the top-left with description: Requests rides, tracks drivers, makes payments.
  • Place "Driver" to the top-right with description: Accepts ride requests, navigates to pickup, completes trips.
  • Place "Support Agent" to the bottom-left with description: Resolves disputes, manages accounts, handles escalations.
  • Place "City Ops Manager" to the bottom-right with description: Configures pricing zones, manages driver supply, monitors KPIs.

Your frame should now show one central platform box surrounded by four labelled people. Each person has a name and a short description explaining their role in the system.

Why does every element need a description?

The C4 Model convention requires every element to have a short description and every arrow to be labeled with its intent. This makes diagrams readable without a presenter standing next to them. A stakeholder six months from now can understand the system just by reading the diagram.

Shapes overlapping or hard to read?

Select all shapes and use Ctrl+Shift+G (Windows) to ungroup if needed. Drag elements apart so each label is fully readable. You can hold Shift while drawing connection lines later to keep them perfectly straight.

Help me arrange my C4 diagram elements in Miro so they don't overlap.

Add external systems with labeled relationships

External systems are software that your platform depends on but does not own. In a C4 System Context diagram, these are drawn with dashed borders to visually distinguish them from your system.

  • Add six dashed-border rectangles around the outside of your Person elements. To create a dashed border, select the rectangle, click the border style option in the context menu, and choose the dashed line style.
  • Label the first external system: Payment Gateway (Stripe).
  • Label the second: Mapping Service (Google Maps Platform).
  • Label the third: Push Notification Service (Firebase Cloud Messaging).

You should now have three dashed rectangles positioned around your diagram. Each one represents a system your platform calls but does not control.

  • Label the fourth external system: SMS/Voice Service (Twilio).
  • Label the fifth: Identity Verification (Onfido).
  • Label the sixth: Government Regulatory API.

Now connect each external system to the central RideFlow Platform box with a unidirectional arrow. Press L to activate the connection line tool, then drag from one shape's blue dot to another.

  • Draw an arrow from RideFlow Platform to Payment Gateway (Stripe). Double-click the line and label it: Processes card charges and refunds via API.
  • Draw an arrow from RideFlow Platform to Mapping Service. Label it: Provides routing, ETAs, and geocoding.
  • Draw an arrow from RideFlow Platform to Push Notification Service. Label it: Delivers real-time alerts to rider/driver mobile apps.
  • Draw an arrow from RideFlow Platform to SMS/Voice Service. Label it: Sends verification codes and trip receipts.
  • Draw an arrow from RideFlow Platform to Identity Verification. Label it: Verifies driver identity documents and background checks.
  • Draw an arrow from RideFlow Platform to Government Regulatory API. Label it: Reports trip data for regulatory compliance.

Every arrow should point in one direction only, and every label should describe the intent of the relationship. "Processes card charges and refunds via API" is specific. A generic label like "uses" is not sufficient.

Why unidirectional arrows with intent labels?

The C4 notation rules state that every line should represent a unidirectional relationship and be labeled consistently with the direction and intent. This removes ambiguity. When someone reads your diagram, they know exactly what data flows where and why.

Arrow pointing the wrong way?

Select the connection line and look for the direction toggle in the context menu. Click it to reverse the arrow direction. Make sure each arrow points FROM the system making the call TO the system providing the service.

Help me fix my arrow directions in my C4 System Context diagram.

Add a diagram key and review against C4 conventions

Every C4 diagram needs a title and a key (legend) explaining the notation. Without these, a reader has to guess what solid vs dashed borders mean.

  • Add a text element at the top of Frame 1. Press T to activate the text tool, then type the diagram title: System Context diagram for RideFlow Platform.
  • Make the title text bold and larger than the shape labels so it reads as a heading.
  • In the bottom-right corner of the frame, draw a small rectangle to serve as your legend box.
  • Inside the legend, add four entries explaining your notation:
  • Solid rectangle = the software system in scope.
  • Dashed rectangle = external software system.
  • Person shape = human actor/role.
  • Arrows = direction of dependency or data flow.

Before you move on, review your completed diagram against the C4 checklist.

  • Confirm every element has both a name AND a short description.
  • Confirm every arrow is unidirectional and has a label matching its direction.
  • Confirm the diagram has a title and a legend.

Before you look at the final result, predict: how many total elements should be visible in your frame? Count the central system, people, external systems, and legend.

You should count 12 main elements: 1 central RideFlow Platform box, 4 Person shapes, 6 external system rectangles, and 1 legend box. Every relationship line is labeled with a specific intent. Your diagram title sits at the top of the frame.

What makes this diagram professional?

A System Context diagram defines what your system IS and IS NOT responsible for. The RideFlow Platform handles ride matching, tracking, and payments. It does NOT handle mapping (Google Maps does that), payment processing infrastructure (Stripe does that), or identity verification (Onfido does that).

This boundary clarity prevents scope creep. When an engineer proposes building a custom mapping engine, you point to this diagram and ask: is that in scope?

Missing elements or labels?

Zoom out to see Frame 1 in its entirety. Count your elements against the checklist: 1 system box, 4 people, 6 external systems, labeled arrows on every connection, 1 legend, and 1 title. If any arrow is missing a label, double-click it and type the relationship intent.

Help me verify my C4 System Context diagram is complete.

Your System Context diagram is complete. You've established the boundaries of the RideFlow Platform and documented every external dependency with specific, directional intent. Next up, you'll zoom inside that central box and decompose it into individual services, databases, and message queues.

Build the Container Diagram

Your System Context diagram established what the RideFlow platform IS and what it connects to. Now the critical question shifts inward: how should the platform itself be structured?

In this step, you'll start with the most intuitive architecture (a single service, single database) and discover why it physically cannot satisfy the competing demands of real-time GPS tracking and financial transaction processing. That tension is the reason microservices exist.

In this step, get ready to:
  • Draw a naive monolith architecture and identify why it fails at scale.
  • Decompose the monolith into five purpose-fit services with different database technologies.
  • Connect Rider and Driver mobile apps through an API Gateway and WebSocket layer.
Start with the naive monolith

The C4 Container diagram zooms inside the system boundary you drew in Step 2. Every container represents a separately deployable unit: a service, a database, or a message queue.

The most intuitive architecture is to put everything in one service with one database. Let's draw that first and see what happens.

  • Navigate to Frame 2 ("Container Diagram (C4 Level 2)") on your Miro board.
  • Draw a large rounded rectangle in the center and label it RideFlow Monolith.
  • Below it, draw a cylinder (database shape) and label it PostgreSQL.
  • Draw arrows from the monolith to the database showing ALL of these responsibilities flowing through a single service: ride matching, GPS tracking, payment processing, notifications, and surge pricing calculations.

Step back and look at what you've drawn. A single PostgreSQL database is now responsible for both thousands of GPS location writes per second AND payment transactions that require strict consistency.

Do you think one database can be optimized for both of these access patterns simultaneously? Think about what happens when geospatial read queries compete with financial write locks.

  • Add a text note element next to the monolith with the following content:
Problem: GPS tracking needs sub-100ms reads across millions of
concurrent drivers. Payment processing needs ACID transactions
with strong consistency. A single PostgreSQL database cannot
optimize for both. Read-heavy geospatial queries with thousands
of writes per second will lock tables needed for payment integrity.

Why start with the monolith?

This is the designed obstacle of the step. In real architecture work, you don't jump straight to microservices. You start with the simplest design and let its limitations reveal the decomposition boundaries.

The monolith fails here because GPS tracking (sub-100ms reads, eventual consistency acceptable) and payment processing (strong consistency, ACID transactions required) have fundamentally incompatible database requirements. This tension is what drives the decomposition you'll draw next.

You should now see a single monolith box with a PostgreSQL database and a prominent problem note explaining why this design breaks at scale.

Decompose into services with purpose-fit databases

Now that you've seen the monolith's limitation, the fix becomes clear: give each domain its own service with a database technology chosen specifically for its access pattern. This is the database-per-service pattern.

  • Below the monolith design (leave it visible for contrast), start drawing the decomposed architecture.
  • Draw the first service: a rectangle labeled Matching Service with technology Python/FastAPI.
  • Connect it to a database labeled Location Store with technology Redis with geospatial indexes.
  • Add a one-line responsibility on the Matching Service: Handles real-time driver proximity queries.

Why Redis for location?

Redis with geospatial indexes processes 100K+ operations per second with sub-millisecond latency. Driver location updates arrive every 3 seconds from potentially millions of concurrent drivers. This write-heavy workload with frequent proximity reads needs in-memory speed that a relational database cannot match.

  • Draw the second service: Trip Service with technology Node.js/Express.
  • Connect it to Trip Database with technology PostgreSQL.
  • Add the responsibility: Manages trip lifecycle and state.
  • Draw the third service: Payment Service with technology Java/Spring Boot.
  • Connect it to Payment Database with technology PostgreSQL with strict ACID.
  • Add the responsibility: Handles charges, refunds, ledger.

You should now have three services visible below the monolith, each paired with its own dedicated database. Notice how the Matching Service uses Redis while the Payment Service uses PostgreSQL with strict ACID. Different problems, different tools.

  • Draw the fourth service: Notification Service with technology Go.
  • Connect it to Push Queue with technology Amazon SQS.
  • Add the responsibility: Async delivery of alerts.
  • Draw the fifth service: Pricing Service with technology Python.
  • Connect it to Pricing Cache with technology Redis.
  • Add the responsibility: Calculates surge pricing in real-time.

Now add the glue that connects all five services together.

  • In the center of your five services, draw a rectangle labeled Event Bus with technology Apache Kafka.
  • Draw arrows from each service to the Event Bus. Label every connection with Kafka events.

Why Kafka as the Event Bus?

Apache Kafka provides ordered, durable event streams with replay capability. When the Payment Service finishes processing a charge, it publishes an event. The Notification Service picks that up and sends a receipt. Neither service needs to know about the other directly.

This decoupling means you can add new services (analytics, fraud detection) without modifying existing ones. It also buffers traffic spikes during surge pricing events.

Your decomposed architecture should now show five services, each with a dedicated data store, all connected through a central Kafka Event Bus. Compare this to the monolith above. The contrast tells the story of why you decomposed.

Show how Rider and Driver apps connect

The services are the internals. Riders and drivers interact through mobile apps that need a single entry point. They should never need to know the internal service topology.

  • At the top of the decomposed architecture, draw a rectangle labeled Rider Mobile App with technology React Native.
  • Next to it, draw another rectangle labeled Driver Mobile App with technology React Native.
  • Below both apps, draw a rectangle labeled API Gateway with technology AWS API Gateway.
  • Draw arrows from both mobile apps down to the API Gateway. Label these connections REST/HTTPS.
  • Draw arrows from the API Gateway down to each of the five services. Label these with the appropriate protocol:

There's one more critical piece. Real-time location streaming between drivers and the Matching Service cannot go through REST. It needs a persistent connection.

  • Draw a rectangle labeled WebSocket Server with technology Node.js/Socket.io.
  • Draw an arrow from the Driver Mobile App to the WebSocket Server. Label it WebSocket.
  • Draw an arrow from the WebSocket Server to the Matching Service. Label it WebSocket.
  • Add a one-line description on the WebSocket Server: "Real-time location streaming between Driver App and Matching Service."

Why a separate WebSocket Server?

WebSocket connections are persistent and bidirectional. REST is request-response. When a driver sends GPS coordinates every 3 seconds, opening a new HTTP connection each time would add unacceptable overhead. The WebSocket Server maintains long-lived connections and streams updates to the Matching Service in real time.

  • Review your complete Container diagram. Confirm that every container has BOTH a technology label AND a one-line responsibility description.
  • Confirm that every connection line has a protocol label.

Before you check the final result, predict: how many distinct database technologies are visible in your decomposed architecture? Count them.

You should count three: Redis (for Matching and Pricing), PostgreSQL (for Trip and Payment), and Amazon SQS (for Notification). Your diagram should show at least 5 separate services, 3 database technologies, and the Apache Kafka Event Bus connecting them all.

Diagram looking cluttered?

If your Frame 2 feels overcrowded, try spacing the services in a rough semicircle with the Event Bus in the center. Keep the monolith design in the upper portion and the decomposed architecture below it.

Make sure you haven't accidentally placed elements outside the frame boundary. Double-click the frame title to re-select it, then drag its edges to expand if needed.

Help me organize my Container diagram layout in Miro.

Your Container diagram now tells a powerful story: the naive approach fails, and here's exactly how we fix it. Next, you'll trace the runtime behavior of this system by mapping the complete ride lifecycle from request to payment.

Map the Data Flow Diagram

Your Container diagram from the last step shows the static structure of RideFlow. It answers what services exist and how they connect. But stakeholders and interviewers always ask the harder question: "Walk me through what happens when a rider requests a ride."

That question demands a Data Flow diagram that traces runtime behavior across services in order. In this step, you will map the complete ride lifecycle from request to payment, proving you understand not just the boxes, but the sequence of interactions between them.

In this step, get ready to:
  • Map the ride request and matching flow (interactions 1-10).
  • Map the trip execution and payment flow (interactions 11-20).
  • Annotate timing budgets and failure/retry points.
Map the ride request and matching flow

The first half of the ride lifecycle covers everything from the moment a rider taps "Request Ride" to the moment driver GPS starts streaming to the rider's phone. This is the time-critical matching phase.

  • Navigate to Frame 3 ("Data Flow - Ride Lifecycle") on your Miro board.
  • Create a sequence/flow diagram with swim lanes (horizontal rows) for these participants: Rider App, API Gateway, Matching Service, Redis (Location Store), Driver App, WebSocket Server.
  • You can create swim lanes using Miro's table or shape tools. Label each row clearly with the participant name.

What are swim lanes?

Swim lanes are horizontal (or vertical) rows that assign each interaction to a specific participant. They make it instantly clear which service is responsible for each action in the flow.

  • Draw numbered arrows between the swim lanes for interactions 1 through 5:
  • Arrow 1: Rider App sends "Request Ride" (pickup, dropoff coordinates) to API Gateway. Label: HTTPS.
  • Arrow 2: API Gateway routes the request to Matching Service.
  • Arrow 3: Matching Service queries Redis using GEORADIUS to find drivers within 2km of pickup.
  • Arrow 4: Matching Service runs matching algorithm (closest available driver considering direction of travel).
  • Arrow 5: Matching Service publishes "RideOffered" event to Kafka. Label: Kafka event.

You should now see 5 numbered arrows flowing across your swim lanes. The flow moves from left to right through the services.

  • Continue with interactions 6 through 10 to complete the matching phase:
  • Arrow 6: WebSocket Server delivers ride offer to Driver App in real-time. Label: WebSocket.
  • Arrow 7: Driver App sends "Accept Ride" to API Gateway.
  • Arrow 8: Matching Service publishes "RideAccepted" event to Kafka.
  • Arrow 9: Trip Service creates trip record (status: DRIVER_EN_ROUTE).
  • Arrow 10: WebSocket Server begins streaming driver GPS to Rider App. Label: WebSocket.

Why does the Matching Service publish events instead of calling services directly?

This is the event-driven architecture pattern from ADR-002. The Matching Service does not need to know that Trip Service or WebSocket Server exist. It publishes events to Kafka, and interested services react independently.

This decoupling means you can add new subscribers (like an analytics service) without changing the Matching Service at all.

Review your diagram so far. You should see 10 numbered interactions flowing across 6 swim lanes, tracing the complete path from ride request to driver GPS streaming.

Map the trip execution and payment flow

The second half covers the ride itself, fare calculation, and payment processing. This part introduces four new participants to your swim lanes.

  • Add four more swim lanes below the existing ones: Trip Service, Payment Service, Pricing Service, and Stripe.
  • Your diagram now has 10 swim lanes total covering every participant in the ride lifecycle.
  • Draw interactions 11 through 15, covering the trip in progress and fare calculation:
  • Arrow 11: Driver App sends "Arrived at Pickup." Trip Service updates status to RIDER_PICKED_UP.
  • Arrow 12: During trip, Driver App streams GPS coordinates every 3 seconds via WebSocket to Location Store (Redis).
  • Arrow 13: Driver App sends "Trip Complete" with final coordinates.
  • Arrow 14: Trip Service calls Pricing Service to calculate fare (base + distance + time + surge multiplier).
  • Arrow 15: Pricing Service returns fare breakdown to Trip Service.

You should see the flow transition from the real-time tracking phase (arrows 11-13) into the calculation phase (arrows 14-15).

  • Complete the payment phase with interactions 16 through 20:
  • Arrow 16: Trip Service calls Payment Service with trip ID and fare amount. Label: REST/HTTPS.
  • Arrow 17: Payment Service calls Stripe API to charge rider's saved card.
  • Arrow 18: Stripe returns charge confirmation to Payment Service.
  • Arrow 19: Payment Service publishes "PaymentComplete" event to Kafka.
  • Arrow 20: Notification Service sends receipt to Rider App and earnings update to Driver App.

What does this diagram prove in an interview?

A data flow diagram proves you understand the runtime behavior of the system. You can explain not just what services exist, but the exact order they communicate, which protocols they use, and what data passes between them.

This is the diagram interviewers ask about when they say "walk me through what happens when a user requests a ride."

Step back and look at your complete diagram. You should see 20 numbered arrows flowing across 10 swim lanes, covering the full lifecycle from ride request to payment receipt.

Annotate with timing and failure points

A good sequence diagram shows the happy path. A great one shows timing constraints and what happens when things break. These annotations are what separate a production-ready architecture document from a basic diagram.

  • Add a timing annotation above interactions 1-10. Text: "Steps 1-10: must complete within 15 seconds total (rider expectation)."
  • Add a timing annotation above interactions 14-18. Text: "Steps 14-18: must complete within 5 seconds (payment flow)."
  • Use a bracket or brace shape spanning the relevant arrows to make it visually clear which interactions fall under each budget.

Why these specific timing budgets?

The 15-second matching budget exists because rider patience drops dramatically after that point. Users abandon requests if matching exceeds 15-20 seconds.

The 5-second payment budget reflects user expectations for card processing. A payment that takes longer feels broken, even if it eventually succeeds.

  • Add failure/retry annotations using a different color (red or orange) to distinguish them from the happy-path flow:
  • Near arrow 3, add a red annotation: "What if no drivers within 2km? Expand radius to 5km, then notify rider of longer wait."
  • Near arrows 17-18, add a red annotation: "What if Stripe charge fails? Retry once, then hold trip in PAYMENT_PENDING and notify rider."
  • Near arrow 10, add a red annotation: "What if WebSocket disconnects? Fallback to polling every 5 seconds."

Color consistency matters

Using a consistent color (like red) for all failure annotations makes them scannable at a glance. Stakeholders reviewing your diagram can instantly spot every failure scenario without reading every label.

Before you check your final diagram, think about what you expect to see. Can you count all 20 interactions? Can you spot the two timing annotations and three failure points?

Zoom out on your Miro board and review Frame 3. You should see a sequence diagram with 10 swim lanes, 20 numbered interactions flowing from ride request to payment receipt, two timing annotation brackets, and three failure/retry annotations in a contrasting color.

Diagram looking cluttered?

If your arrows overlap or labels are hard to read, try increasing the vertical spacing between swim lanes. You can also use smaller font sizes for the failure annotations while keeping the main flow labels larger.

Make sure each arrow number is clearly visible. The numbered sequence is what makes this diagram readable when presenting to stakeholders.

Help me improve my diagram layout

Your data flow diagram is complete. You can now walk anyone through the exact runtime behavior of RideFlow, interaction by interaction, including what breaks and how the system recovers. Next up, you will model the data itself with an Entity Relationship Diagram and document every major decision with Architecture Decision Records.

Design the ERD and Write ADRs

Your Data Flow diagram traces every interaction across the ride lifecycle. Now it's time to prove you think at the data layer itself, not just the service boxes.

The Entity Relationship Diagram defines what data your platform stores and how tables relate to each other. Architecture Decision Records document WHY you chose each technology over alternatives. Together, these are the single biggest signal hiring managers look for in architecture interviews.

In this step, get ready to:
  • Design a complete ERD with 7 entities covering users, trips, payments, and pricing.
  • Write 5 Architecture Decision Records with trade-off analysis for every major technology choice.
Design the Entity Relationship Diagram

An ERD models the actual data your platform persists. Each entity becomes a table in a database, with primary keys (PK) uniquely identifying rows and foreign keys (FK) linking tables together.

  • Navigate to Frame 4 ("Entity Relationship Diagram") on your Miro board.
  • Create 7 entity boxes (rectangles). Each entity needs a title row with the entity name, followed by rows listing every attribute.

Add the following 7 entities with their attributes. Mark each primary key with "PK" and each foreign key with "FK":

  • Users — user_id PK, email, phone, full_name, role ENUM[rider/driver/both], created_at.
  • Drivers — driver_id PK, user_id FK, vehicle_id FK, license_number, verification_status, rating_avg, is_online, current_location_lat, current_location_lng.
  • Vehicles — vehicle_id PK, driver_id FK, make, model, year, color, license_plate, vehicle_type ENUM[economy/premium/xl].
  • Trips — trip_id PK, rider_id FK, driver_id FK, status ENUM[requested/matched/en_route/in_progress/completed/cancelled], pickup_lat, pickup_lng, dropoff_lat, dropoff_lng, requested_at, started_at, completed_at, distance_km, duration_minutes.
  • Payments — payment_id PK, trip_id FK, rider_id FK, amount_cents, currency, stripe_charge_id, status ENUM[pending/completed/failed/refunded], created_at.
  • Ratings — rating_id PK, trip_id FK, from_user_id FK, to_user_id FK, score (1-5), comment, created_at.
  • Pricing_Zones — zone_id PK, city, base_fare_cents, per_km_cents, per_minute_cents, surge_multiplier, active_from, active_until.

You should now see 7 entity boxes in Frame 4, each with a clear title and attribute list.

Why store amounts in cents?

Storing monetary values as integers (cents) avoids floating-point rounding errors. $12.50 becomes 1250 in the database. This is a standard pattern in payment systems.

Now draw relationship lines between entities and label each with its cardinality:

  • Draw a line from Users to Drivers and label it "1:1" (a driver IS a user with extra attributes).
  • Draw a line from Drivers to Vehicles and label it "1:N" (a driver can have multiple registered vehicles).
  • Draw a line from Users to Trips and label it "1:N (as rider)." Draw another line from Drivers to Trips and label it "1:N (as driver)."
  • Draw a line from Trips to Payments and label it "1:1" (each trip has exactly one payment).
  • Draw a line from Trips to Ratings and label it "1:N" (rider rates driver, driver rates rider).

Step back and review Frame 4. You should see 7 entity boxes connected by labeled relationship lines, with cardinality notation on each.

Why doesn't Pricing_Zones connect to other entities?

Pricing_Zones is a reference table used at runtime by the Pricing Service to calculate fares. It doesn't have a direct foreign key relationship to Trips because the fare is calculated and stored as a value in the Payments table, not as a join.

Diagram looking cluttered?

Place the Trips entity in the center since it connects to the most other entities. Arrange Users and Drivers to one side, Payments and Ratings to the other. Use Miro's alignment tools to snap entities to a grid.

Help me organize my ERD layout in Miro.

Write Architecture Decision Records

An ADR captures a single architecture decision with the reasoning behind it. Each record answers: "What did we decide, what else did we consider, why this option, and what are the trade-offs?"

  • Navigate to Frame 5 ("Architecture Decision Records") on your Miro board.
  • Create your first ADR card as a large sticky note or text box. Use this structure for every ADR: a title, then four sections labeled Decision, Alternatives Considered, Rationale, and Consequences.

Create ADR-001 with the following content:

  • Title: ADR-001: Database per Service
  • Decision: Each microservice owns its own database. No shared database.
  • Alternatives Considered: Shared PostgreSQL with schemas per service. Single database with strict access control.
  • Rationale: Location data needs Redis geospatial indexes (GEORADIUS, sub-ms reads). Payments need PostgreSQL ACID transactions. A single database cannot optimize for both access patterns at scale. Independent scaling and deployment is required.
  • Consequences: Need eventual consistency between services. Must implement saga pattern for cross-service transactions. Data duplication accepted for read performance.

You should see your first ADR card complete with all four sections filled in. Now create ADR-002:

  • Title: ADR-002: Apache Kafka as Event Bus
  • Decision: Use Apache Kafka for asynchronous inter-service communication.
  • Alternatives Considered: RabbitMQ (simpler but less durable). Amazon SQS (managed but no replay). Direct REST calls between services.
  • Rationale: Kafka provides ordered, durable event streams with replay capability. Critical for audit trails (every state change is an event), decoupling services (Payment Service doesn't need to know about Notification Service), and handling traffic spikes (buffer during surge pricing events).
  • Consequences: Added operational complexity. Team needs Kafka expertise. Events must be designed carefully (schema evolution via Avro/Protobuf).

Create ADR-003:

  • Title: ADR-003: Redis for Real-Time Location
  • Decision: Use Redis with geospatial data structures for driver location storage and proximity queries.
  • Alternatives Considered: PostgreSQL with PostGIS (proven but slower at scale for write-heavy workloads). MongoDB with 2dsphere indexes (viable but less performant for our read/write ratio). Elasticsearch with geo_point.
  • Rationale: Redis GEOADD/GEORADIUS processes 100K+ operations/second with sub-millisecond latency. Driver location updates arrive every 3 seconds from potentially millions of concurrent drivers. This is a write-heavy workload with frequent proximity reads that needs in-memory speed. Acceptable trade-off: data is ephemeral (only current location matters, not history).
  • Consequences: Data is volatile (lost on restart without persistence config). Need Redis Cluster for horizontal scaling. Location history for trip reconstruction must be stored separately (in Trip Service's PostgreSQL).

You should now have 3 ADR cards in Frame 5, each clearly structured with all four sections.

What makes a good ADR?

The power of an ADR is in the Alternatives and Consequences sections. Listing what you considered and rejected proves you made a deliberate choice. Naming consequences honestly shows you understand the trade-offs, not just the benefits.

In an interview, saying "we chose Kafka because it's popular" fails. Saying "we chose Kafka over SQS because we need replay for audit trails, and over RabbitMQ because we need durability during surge events" lands.

Add ADR-004 and ADR-005 for completeness

Two more ADRs round out your architecture document, covering the entry point and the coordination strategy.

Create ADR-004:

  • Title: ADR-004: API Gateway Pattern
  • Decision: Single API Gateway (AWS API Gateway) as the entry point for all client requests.
  • Alternatives Considered: Client-side service discovery (complex for mobile). BFF (Backend for Frontend) per client type. Service mesh with direct client access.
  • Rationale: Mobile apps should not need to know about internal service topology. Gateway handles auth, rate limiting, and routing. Simplifies client development and enables independent backend service evolution.
  • Consequences: Gateway is a single point of failure (mitigated by AWS managed service SLA). Added latency hop (~5-15ms). Must avoid "smart gateway" anti-pattern (keep routing logic simple).

Create ADR-005:

  • Title: ADR-005: Event-Driven Saga for Trip Lifecycle
  • Decision: Use choreography-based saga pattern (via Kafka events) for the trip lifecycle rather than orchestration.
  • Alternatives Considered: Orchestration saga (central Trip Coordinator service). Two-phase commit (too slow). Synchronous REST chain (brittle).
  • Rationale: Each service reacts to events and publishes its own events. No single service needs to know the full workflow. Better resilience: if Notification Service is down, the trip still completes and notifications catch up via Kafka replay.
  • Consequences: Debugging distributed flows is harder. Need correlation IDs on every event. Must implement compensating transactions for failure scenarios (e.g., if payment fails after trip completes, trigger dispute flow).

Before you check your work, do you think 5 ADRs is enough to cover the major architecture decisions in this platform? What might be missing?

Review Frame 5. You should see 5 ADR cards arranged clearly, each with the title and all four sections filled in. Every card documents a different technology choice with at least 2 alternatives considered and specific consequences named.

What else could be an ADR?

Other decisions worth recording include: why React Native for both mobile apps (code sharing vs native performance), why PostgreSQL over MySQL for the Trip and Payment databases, and why choreography over orchestration sagas. The 5 you've written cover the most impactful decisions.

ADR cards looking too dense?

If the text is hard to read, try using Miro's text formatting to bold the section headers (Decision, Alternatives Considered, Rationale, Consequences). You can also use different background colors for each ADR to visually separate them.

Help me format my ADR cards in Miro for better readability.

Secret mission

Non-Functional Requirements Matrix

Your architecture diagrams show what you're building, but can you prove the system holds up under real-world load? Add a Non-Functional Requirements matrix with specific SLA targets, latency budgets, and throughput estimates for each service. This is what separates a diagram from a production-ready specification.

Clean Up Your Resources

Clean Up Your Resources

This project runs entirely on Miro's cloud platform. Decide whether to keep your board accessible, cancel your trial before it converts to a paid plan, or delete your board entirely.

Cost warning

If you started the 14-day Miro Business free trial, it will automatically convert to a paid subscription ($25/month billed monthly, or $20/month billed annually) when the trial ends. Cancel before the 14-day period ends to avoid charges.

Resources you used:

  • Miro account (Business plan or free trial).
  • Miro board: "RideFlow Platform - Solution Architecture" with 6 frames.

Keep everything running

No action needed. Choose this if you plan to continue using this board as a portfolio piece or want to extend it in Parts 2 and 3 of this series.

  • Your Miro board remains accessible and editable as long as your Business plan (or trial) is active.
  • If your trial converts to a paid plan, you keep full access to the AWS shape packs and all 6 frames.
  • Export your diagrams before your trial ends if you are unsure whether you want to continue paying. Click the board menu icon in the top-left toolbar, then click Export, and choose PNG or PDF.

Pause - I'll come back to this later

Cancel your Miro Business trial to avoid charges, but keep your board for later. Your board will become view-only after the trial ends. You can reactivate anytime.

  • Before canceling, export your diagrams as a backup. Click the board menu icon in the top-left toolbar, then click Export, and save as PNG or PDF.
  • Click your profile avatar in the top-right corner, then click Profile settings.
  • Go to Billing.
  • In the Licensing configuration section, click Cancel trial.
  • Click Continue, then select Cancel trial and click Cancel subscription.
  • Select a reason for canceling and click Cancel trial.
  • After the trial period ends, your board enters view-only mode. You can revert to the Free plan or reactivate a paid plan at any time.

What happens on the Free plan?

On the Free plan, you keep existing boards but only your three most recently created boards remain editable. You also lose access to the AWS shape packs for future edits. Your diagrams are still viewable.

Delete - I don't want to use this again

Remove your board and cancel your Miro trial completely. This frees you from any future charges and removes the board from your account.

  • First, export your diagrams if you want a copy. Click the board menu icon in the top-left toolbar, then click Export, and save as PNG or PDF.
  • To delete the board: go to your Miro dashboard. Find the "RideFlow Platform - Solution Architecture" board, click the three dots (...) menu on the board, and select Delete.
  • The board moves to Trash. To permanently delete it, click your profile avatar in the upper right, click the Trash icon, find the board, click the three dots (...) menu, and select Delete permanently. Confirm by checking the box and clicking Delete permanently.
  • To cancel your trial: click your profile avatar in the top-right corner, then click Profile settings.
  • Go to Billing.
  • In the Licensing configuration section, click Cancel trial.
  • Click Continue, then select Cancel trial and click Cancel subscription.
  • Select a reason for canceling and click Cancel trial.

Permanent deletion cannot be undone

Once you permanently delete a board from Trash, it cannot be recovered. Make sure you have exported any diagrams you want to keep before confirming permanent deletion.

That's a Wrap!

That's a Wrap!

Nice work! You've just architected a real-time ride-hailing platform from the ground up, producing the exact deliverable a solutions architect presents before a single line of code gets written.

You've learned how to:

  • Decompose a monolith into microservices driven by actual technical constraints, learning why different data access patterns demand different database technologies.
  • Traced a complete ride lifecycle across 6+ services using a Data Flow diagram, including 20 interactions, failure scenarios, and timing budgets that prove you understand runtime behavior.
  • Documented 5 Architecture Decision Records with trade-off analysis, proving you can justify every technology choice with alternatives considered and consequences accepted.
  • Secret Mission: Quantified non-functional requirements with specific SLA targets, throughput estimates, and recovery objectives for each service, proving you think about systems under real-world production load.

Ready to quiz yourself?