Clean Code and Clean Architecture

Author: Himanshu Garg | Date: August 3, 2025

Clean Code and Clean Architecture

I have been a great fan of the Clean Code, made famous by Robert Cecil Martin AKA Uncle Bob by his book of the same name. In one of his clean code lectures he gave an example of Civil Engineering. He pointed out that if we were to design the architecture of a Church, the first thing we would think would be the size of the prayer hall, the seating capacity and the routes not the construction materials to use like bricks and cement. Similary, we should think about software from the same perspective. That gave the clarity for the purpose and design choices of clean code.

First Things First, clean code is not only an architectue, it is a philosophy. It not only limits to how the code flow works it guides on how to keep things simple, well-organized, self-explanatory reducing unnecessary complexity. It also include coding style, functions and class naming.

Let me explain how I designed the architecture using simple yet modular components. I adopted the standard three-layer architecture recommended by Clean Code. Initially, I implemented a more nuanced four-layer architecture, but after carefully considering the trade-offs between added complexity and maintainability given our fast-paced development I graduated it to three layers.

A Quick Overview of Clean Architecture

I will quickly go through each layer of the architecture

Clean Architecture Overview
  1. Data Layer
    This is a framework specific layer. It implements the framework specific logic to data operations. The data operation can include but not limited to getting data from api, writing to preference storage, local data base operations. This layer has framework specific data holder (called data class in Kotlin) classes which are not passed to other layers for separating out framework details.
  2. Domain Layer
    This layer is where all the core business logic lives, totally independent of any frameworks. It sits right at the center of the app. This is where you put what the business needs. "Clean Code" stresses on the idea of use cases, each one represents a particular bit of functionality the app should have. The usecases work on framework independent data classes that are declared only in Domain Layer. The data layer doesn't add any framework specific data classes directly to domain layer. Instead, it uses mapper functions to convert any framework specific data holders into plain, framework independent ones before they reach this layer. That way, the core logic isn't tied to any platform.
  3. Presentation Layer
    This layer is all about the UI. It is responsible for showing everything the user sees and interacts with. Just like the data layer, it doesn't pass any framework specific objects down to the domain layer, keeping the core logic free from UI or framework details.
    In the case of MVVM (Model-View-ViewModel), this layer includes the ViewModel, the UI components (like screens or widgets), and the UI state objects that track what's currently shown or interacted with. The ViewModel acts as a middleman between the UI and the domain layer, managing state and exposing user interface data.

Indilingo App Architecture

Indilingo uses MVVM with clean architecture. The clear separation of concerns and the maintainability that clean architecture offers far outweigh the extra effort required to write code in clean architecture. Even though we've been iterating at an unusually fast pace, I've been careful to uphold the core principle of clean architecture; never introducing framework specific details into the implementation.

How this helped us?

When we decided to move to KMP (Kotlin Multiplatform, which is a framework made to write app for all platforms including android, iOS and desktop), the domain layer never needed to change. The exact logic worked with updating the imports without the need for change in any other parts of the code. KMP uses the same UI framework as android (Jetpack Compose) which let us reuse the UI logic, i.e., we were able to reuse most of our presenation layer. Significant changes were required only in the data layer. We implemented the data layer and updated the mappers such that they worked on the same framework independent data classes from domain layer. Consequently, we were able to migrate most parts of our app to KMP within a week (which beat our estimates).

Takeaways

This is one of the examples of real world benefits of "Clean Code".
Following the principles diligently helped us to quickly migrate to a new framework without the need of rewriting the entire codebase from scratch.