Modern guide to app architecture

Common architectural principles

As Android apps grow in size, it’s important to define an architecture that allows the app to scale, increases the app’s robustness, and makes the app easier to test. We you should design your app architecture to follow a few specific principles.

Separation of concerns

It’s a common mistake to write all your code in an Activity or a Fragment. These UI-based classes should only contain logic that handles UI and operating system interactions. By keeping these classes as lean as possible, you can avoid many problems related to the component lifecycle, and improve the testability of these classes.

Drive UI from data models

Another important principle is that you should drive your UI from data models, preferably persistent models. Data models represent the data of an app. They’re independent from the UI elements and other components in your app. 

Persistent models are ideal for the following reasons:

  • Your users don’t lose data if the Android OS destroys your app to free up resources.
  • Your app continues to work in cases when a network connection is flaky or not available.

Single source of truth

When a new data type is defined in your app, you should assign a Single Source of Truth (SSOT) to it. The SSOT is the owner of that data, and only the SSOT can modify or mutate it. To achieve this, the SSOT exposes the data using an immutable type, and to modify the data, the SSOT exposes functions or receive events that other types can call.

This pattern brings multiple benefits:

  • It centralizes all the changes to a particular type of data in one place.
  • It protects the data so that other types cannot tamper with it.
  • It makes changes to the data more traceable. Thus, bugs are easier to spot.

Unidirectional Data Flow

The single source of truth principle is often used in our guides with the Unidirectional Data Flow (UDF) pattern. In UDF, state flows in only one direction. The events that modify the data flow in the opposite direction.

In Android, state or data usually flow from the higher-scoped types of the hierarchy to the lower-scoped ones. Events are usually triggered from the lower-scoped types until they reach the SSOT for the corresponding data type. 

Recommended app architecture

UI layer that displays application data on the screen.

  • UI elements that render the data on the screen. You build these elements using Views or Jetpack Composefunctions.
  • State holders (such as ViewModel classes) that hold data, expose it to the UI, and handle logic.

Data layer of an app contains the business logic

he business logic is what gives value to your app—it’s made of rules that determine how your app creates, stores, and changes data.

The data layer is made of repositories that each can contain zero to many data sources. You should create a repository class for each different type of data you handle in your app. For example, you might create a MoviesRepository class for data related to movies, or a PaymentsRepository class for data related to payments.

 Domain layer is an optional layer that sits between the UI and data layers.

The domain layer is responsible for encapsulating complex business logic, or simple business logic that is reused by multiple ViewModels. This layer is optional because not all apps will have these requirements.

Designing modern app artitecture

1. Understand Requirements:

Before diving into architecture design, thoroughly understand the requirements of your app, including functionality, performance, scalability, and maintainability.

2. Choose an Architecture Pattern:

There are several architecture patterns commonly used in app development:

  • MVC (Model-View-Controller): Separates the app into three components: Model (data), View (UI), and Controller (logic). Often leads to Massive View Controller problem.
  • MVP (Model-View-Presenter): Similar to MVC but separates the presentation logic (Presenter) from the UI (View).
  • MVVM (Model-View-ViewModel): Separates UI (View) from business logic (ViewModel) using data binding.
  • MVI (Model-View-Intent): Unidirectional data flow architecture that emphasizes immutability and state management.
  • Clean Architecture: Separates the app into layers (UI, Domain, Data) with clear boundaries and dependencies.

Choose the architecture pattern that best suits the requirements and complexity of your app.

3. Design Principles:

Regardless of the architecture pattern chosen, adhere to design principles such as:

  • Single Responsibility Principle (SRP): Each class or component should have a single responsibility.
  • Separation of Concerns: Separate different aspects of the app (e.g., UI, business logic, data access) into distinct modules.
  • Dependency Injection: Inject dependencies into classes rather than hardcoding them, promoting loose coupling and testability.

4. Components of App Architecture:

  • UI Layer: Contains the presentation logic, including activities, fragments, views, and view models (in MVVM or MVI).
  • Domain Layer (Business Logic): Contains the core business logic and use cases of the app. It’s independent of any UI or data layer.
  • Data Layer: Handles data retrieval and storage, including remote data sources (APIs), local data sources (database), and repositories.

5. Libraries and Frameworks:

Choose libraries and frameworks that complement your architecture:

  • Android Jetpack: Provides a set of libraries for common tasks like navigation, lifecycle management, and data persistence.
  • Retrofit: For making network requests and interacting with APIs.
  • Room: For local database storage and data access.
  • Dagger or Koin: For dependency injection.
  • RxJava or Kotlin Coroutines: For asynchronous and reactive programming.

6. Testing:

Implement a robust testing strategy to ensure the reliability and stability of your app:

  • Unit Tests: Test individual components (e.g., classes, functions) in isolation.
  • Integration Tests: Test interactions between different components or layers.
  • UI Tests: Test the app’s user interface and user interactions.

7. Scalability and Maintainability:

Design your architecture to be scalable and maintainable over time:

  • Modularity: Divide your app into modules based on features or functionality to facilitate independent development and testing.
  • Documentation: Document your architecture, design decisions, and codebase to aid understanding and maintenance.
  • Code Reviews: Conduct regular code reviews to ensure adherence to architectural guidelines and best practices.

By following these guidelines, you can design an app architecture that meets the requirements of your project while providing scalability, maintainability, and testability.

Modern App Architecture

This Modern App Architecture encourages using the following techniques, among others:

  • A reactive and layered architecture.
  • Unidirectional Data Flow (UDF) in all layers of the app.
  • A UI layer with state holders to manage the complexity of the UI.
  • Coroutines and flows.
  • Dependency injection best practices.

Leave a Reply