Most Clean Architecture tutorials hand you a diagram and hope. This one makes you build a ride-sharing app one file at a time and decide, for every piece, which layer it belongs in. Guess. Get it wrong. Watch the dependency arrows turn red. Then never forget.
01 · The map
A pragmatic take, not the textbook five. Use cases fold into Domain; adapters and infrastructure merge into one Data layer. The rule: everything points at Domain, and Domain points at nothing. Organise by feature, not by type. Click any layer to open it up.
Dependencies point inward. Data and Presentation both depend on the Domain core, never on each other. Framework is the replaceable shell the outer layers plug into — and Domain never does.
02 · Where does it live?
Real problems from the ride-sharing app. Pick where each piece of code belongs. You'll get the reasoning either way — being wrong here is how it sticks.
03 · Build it, one file at a time
Start from an empty project. Each step adds exactly one file, and shows the architect's thought process behind it — the reasoning that turns memorising layers into designing them.
04 · Follow the request
The rider taps “Request.” Step through exactly where control goes, and where a raw JSON payload becomes a clean Ride your UI can trust.
05 · Prove it
Drag each component into the layer where it belongs — or tap a piece, then tap a layer. Put URLSession in Domain, or a use case in Presentation, and you'll see exactly why the architecture won't allow it.
06 · How we got here
Clean Architecture isn't dogma for its own sake. It's the endpoint of a decade of iOS teams feeling the same pain and factoring it out. Watch the coupling drop.
App entry point or a per-feature factory — you construct the concrete RideAPI, inject it into RideRepositoryImpl, inject that into RequestRideUseCase, and hand the use case to the RideStore. Every arrow is resolved in exactly one place, so swapping Firebase for Supabase is a one-file change.
RideRepository — and that abstraction is owned by the inner layer. The concrete implementation in the Data layer conforms up to it. That inversion is the whole trick, and it's the one place a protocol earns its keep.
RideRepositoryImpl an actor to serialise cache access without locks, back it with an offline-first store (return cached rides instantly, revalidate in the background via an AsyncStream), and keep all of that invisible to the use case — it still just calls repository.requestRide(...).
Pin board
The five things that, if you remember nothing else, keep any codebase clean.
You've seen why each layer exists. Want a senior to review your architecture, or run a mock system-design interview on exactly this? First session is free.
Book a free 1:1 Connect on LinkedIn