Back to blog
Architecture

Clean Architecture in Mobile: Theory vs Reality

March 5, 202410 min read

The Promise

Robert C. Martin's Clean Architecture promises a codebase that is:

  • Independent of frameworks
  • Testable without UI or database
  • Independent of the database
  • Independent of any external agency

After 12 years across dozens of mobile projects, from small MVPs to large enterprise apps, I have opinions about when it works and when it doesn't.

When Clean Architecture Shines

Large Teams (5+ Developers)

Several developers work on the same app at Jio Health with few merge conflicts, because the boundaries are clear:

  • The **domain layer** defines business rules. It doesn't import UIKit, Flutter, or any framework.
  • The **data layer** implements repositories and data sources. Swapping the API doesn't touch the UI.
  • The **presentation layer** handles UI logic. You can redesign a screen without knowing the data pipeline.

Projects That Last

Our patient app at Jio Health has been in active development since 2019. Over 5 years:

  • Migrated from Objective-C to Swift
  • Added a Flutter module for new features
  • Switched from REST to gRPC for certain services
  • Replaced Realm with Core Data in some modules

Each change stayed inside one layer. The domain layer, our business rules, didn't change.

Apps with Complex Business Logic

Healthcare rules are complex: appointment booking with timezone handling, insurance verification, prescription validation, video call scheduling. In a domain layer with no framework imports, we test them exhaustively: no simulators, no mocks of UIKit classes.

When Clean Architecture is Overkill

Small Apps or MVPs

Clean Architecture will slow down an MVP. At ShipDauRoi, my startup, the first version had 5 screens (login, map, order list, order detail, profile) and I spent 2 weeks setting up layers before writing a feature. Those 2 weeks should have gone into shipping.

Rule of thumb: Under 10 screens and 1 to 2 developers, start with MVVM and refactor later if it grows.

CRUD Apps

If your app mostly fetches data from an API and displays it, skip the domain layer and use cases; a repository pattern with MVVM is enough.

Solo Developer Projects

When you're the only developer, separate layers, protocols, and dependency injection are overhead: you already know the whole codebase, so the abstractions communicate nothing.

Common Mistakes I've Seen (and Made)

Abstracting Everything

I've seen codebases where every class has a protocol, every protocol has one implementation, and every call goes through 3 layers of indirection. Harder to read, not easier.

My approach now: Only abstract when you have or expect multiple implementations. A `UserRepository` protocol makes sense if you have a `RemoteUserRepository` and a `MockUserRepository`. A `UserNameFormatter` protocol with one implementation is noise.

Use Cases That Do Nothing

Common pattern:

class GetUserUseCase {
  let repository: UserRepository
  func execute(id: String) -> User {
    return repository.getUser(id: id)
  }
}

It just forwards the call. Give it real business logic or call the repository directly.

When use cases add value: Coordinating multiple repositories, applying business rules, transforming data.

Ignoring the Framework

Clean Architecture says "be independent of frameworks." Taken literally, you reimplement what the framework already gives you.

Core Data and SwiftUI have opinions about architecture, and fighting them creates friction. Isolate framework dependencies at the boundaries and keep the inner layers free.

My Practical Architecture

What I actually use:

For Large Projects (5+ devs, 20+ screens)

  • Domain layer:: Entities, use cases, repository protocols. No framework imports.
  • Data layer:: Repository implementations, API services, local storage. Framework dependencies allowed.
  • Presentation layer:: ViewModels/BLoCs, Views/Widgets. Framework code belongs here.
  • Dependency injection:: Constructor injection, no DI frameworks. A composition root is enough.

For Medium Projects (2 to 4 devs, 10 to 20 screens)

  • MVVM with repository pattern.: No separate domain layer.
  • Protocols for repositories: (to enable testing).
  • Simple dependency injection: via factory methods.

For Small Projects / MVPs

  • MVVM or MVC.: Keep it simple.
  • No abstractions: until you feel the pain.
  • Ship fast,: refactor when the product proves itself.

Key Takeaways

  • Architecture should match your project's complexity.: Don't apply enterprise patterns to an MVP.
  • Abstractions have a cost.: Every protocol, layer, and indirection adds overhead.
  • The domain layer is the most valuable part.: Adopt one thing: a domain layer with no framework imports.
  • Refactoring is not failure.: Start simple and add architecture as complexity grows.
  • Test your business logic, not your architecture.: If tests break on every refactor, they're coupled to the wrong things.

Architecture is a tool, not a goal. Use it to serve your team and product.

Read next

How I Built ShipDauRoi and Reached Top 50 on the App Store

The story from idea to real product, reaching Top 50 and the valuable startup lessons along the way.