Introduction to Clean Architecture

Why Clean Architecture ?


Clean Architecture is a software design approach that emphasizes separation of concerns, promoting modularity, testability, and maintainability by organizing code into distinct layers with clear boundaries between them. It prioritises the independence of core business logic from external frameworks, tools, and implementation details. For an example Clean architecture is like building a house with different layers: the inside layer is where the important stuff like bedrooms and bathrooms are, the middle layer is for how those rooms are connected, and the outer layer is for adding windows, doors, and paint. Each layer does its own job, making it easy to change things without messing up the whole house.


Clean Architecture offers several advantages, making it a popular choice for designing software systems:

  1. Maintainability: Clean Architecture promotes separation of concerns, which makes it easier to understand and maintain the codebase over time. With clear boundaries between layers and modules, developers can make changes to one part of the system without impacting others, reducing the risk of unintended consequences.
  2. Testability: By isolating the core business logic in the inner layers, Clean Architecture facilitates unit testing. Components such as use cases and entities can be tested independently of external dependencies, leading to more comprehensive test coverage and improved software quality.
  3. Flexibility and Adaptability: Clean Architecture enables developers to swap out external dependencies, such as databases or user interface frameworks, without affecting the core business logic. This flexibility allows for easier integration of new technologies, upgrades, or changes in requirements over time.
  4. Scalability: Clean Architecture provides a modular structure that scales well as the complexity of the system grows. With clear boundaries between layers, it’s easier to add new features, refactor existing code, or extend functionality without introducing unnecessary complexity.
  5. Reduced Coupling: Clean Architecture minimizes coupling between components by enforcing a strict dependency hierarchy. This reduces the risk of cascading changes and makes the system more resilient to changes in external dependencies or requirements.
  6. Improved Code Quality: By adhering to SOLID principles and best practices, Clean Architecture promotes clean, readable, and maintainable code. Clear separation of concerns, proper abstraction, and modular design contribute to improved code quality and developer productivity.
  7. Portability: Clean Architecture makes it easier to port the application to different platforms or environments. Since the core business logic is decoupled from external dependencies, the same business rules can be reused across different interfaces or implementations.
  8. Enforces Best Practices: Clean Architecture encourages adherence to best practices such as separation of concerns, dependency inversion, and encapsulation. This leads to more robust, stable, and maintainable software systems in the long run.

At its core, Clean Architecture revolves around the idea of organizing code in concentric circles or layers, with each layer representing a different level of abstraction and responsibility. The key layers typically include:

  1. Entities: This innermost layer contains the core business entities or domain objects. Entities encapsulate the most fundamental business rules and behaviors of the application. They are independent of any external frameworks, databases, or user interfaces.
  2. Use Cases (Interactors): Use cases represent the application-specific business rules and logic. Each use case corresponds to a specific action or task that users can perform within the system. Use cases orchestrate the flow of data and interactions between entities and other layers. They encapsulate the application’s business logic in a way that is isolated from external concerns.
  3. Interface Adapters: This layer contains adapters that convert data between the formats most convenient for the use cases and the external entities such as databases, web services, or user interfaces. Adapters translate data and events from external sources into formats that are usable by the use cases, and vice versa. This layer ensures that changes in external dependencies do not affect the core business logic of the application.
  4. Frameworks and Drivers: This outermost layer includes all external frameworks, tools, and libraries used by the application. This layer contains components such as web frameworks, databases, user interface libraries, and other infrastructure-related dependencies. The key principle is that the inner layers should not depend on the outer layers, allowing for flexibility and interchangeability of external components.

Clean Architecture promotes several key principles:

  • Dependency Rule: Dependencies should point inward, with the inner layers depending on the outer layers. This ensures that changes in external components do not impact the core business logic.
  • Abstraction: The core business logic should be abstracted and decoupled from implementation details. This allows for easier testing, maintenance, and evolution of the system.
  • SOLID Principles: Clean Architecture encourages adherence to SOLID principles (Single Responsibility, Open/Closed, Liskov Substitution, Interface Segregation, and Dependency Inversion), which promote modular, maintainable, and extensible code.

By adhering to Clean Architecture principles, developers can create systems that are easier to understand, maintain, and evolve over time. Clean Architecture fosters modularity, testability, and flexibility, enabling developers to build robust and scalable software applications.

Let’s break down Clean Architecture with an Android example for a simple note-taking application:

  1. Entities:
    • These are the core business objects representing domain concepts. In our example, we might have an entity like Note, which contains attributes such as id, title, content, and timestamp. These entities are independent of any specific Android framework or database implementation.
  2. Use Cases (Interactors):
    • Use cases encapsulate the application-specific business logic. For our note-taking app, we might have use cases like GetNotes, AddNote, DeleteNote, etc. Each use case would perform a specific action, such as fetching notes from a repository, adding a new note, or deleting an existing note.
  3. Interface Adapters:
    • This layer contains adapters that interact with external frameworks, databases, or user interfaces. In Android, these adapters could include:
      • Presenters: Convert data from use cases into a format suitable for displaying on the UI, and vice versa.
      • Repositories: Interface between the use cases and data sources (e.g., local database, remote API). Repositories abstract the data access implementation details.
      • Controllers or ViewModels: Handle user interactions and delegate actions to the appropriate use cases.
      • Data Sources: Implementations for accessing data, such as Room for local database storage or Retrofit for remote API calls.
  4. Frameworks and Drivers:
    • This outermost layer consists of Android-specific frameworks and libraries. It includes components such as:
      • UI Layer: Activities, Fragments, Views, and Layouts for presenting the user interface.
      • Persistence Layer: Room Database, SharedPreferences, or SQLiteOpenHelper for local data storage.
      • Network Layer: Retrofit or Volley for making network requests to remote servers.
      • Dependency Injection Framework: Dagger or Hilt for managing dependencies between components.
- app
  - src
    - main
      - java/com/example/noteapp
        - entities
          - Note.java
        - usecases
          - AddNote.java
          - DeleteNote.java
          - GetNotes.java
        - adapters
          - presenters
            - NotePresenter.java
          - repositories
            - NoteRepository.java
          - controllers (ViewModels)
            - NoteListViewModel.java
            - AddNoteViewModel.java
          - datasources
            - LocalNoteDataSource.java
            - RemoteNoteDataSource.java
      - ui
        - activities
          - MainActivity.java
          - AddNoteActivity.java
        - fragments
          - NoteListFragment.java
          - NoteDetailFragment.java
        - adapters
          - NoteListAdapter.java
      - di (Dependency Injection)
        - AppComponent.java
        - AppModule.java
        - NetworkModule.java
        - RepositoryModule.java
      - data
        - database
          - NoteDatabase.java
          - NoteDao.java
        - network
          - ApiService.java
        - repository
          - NoteRepositoryImpl.java
      - utils
        - ...
    - res
      - layout
        - activity_main.xml
        - activity_add_note.xml
        - fragment_note_list.xml
        - fragment_note_detail.xml
      - ...

In this structure:

  • Entities (e.g., Note) reside in the entities package.
  • Use Cases (e.g., AddNote, GetNotes) are placed in the usecases package.
  • Interface Adapters such as presenters, repositories, controllers, and data sources are organized under the adapters package.
  • Frameworks and Drivers components like activities, fragments, and adapters for UI are under the ui package, while dependency injection related code goes into the di package, and data-related components such as database, network, and repository implementations are under the data package.

This structure adheres to Clean Architecture principles by keeping the core business logic isolated and independent of external frameworks and tools, thus promoting maintainability, testability, and flexibility in our Android note-taking application.

Leave a Reply