iTechGuides is reader-supported. When you buy through links on our site, we may earn an affiliate commission. As an Amazon Associate I earn from qualifying purchases. Learn more
A growing mobile product needs clear boundaries more than it needs a fashionable pattern. For an Android app, start with a UI layer and a data layer, put repositories between the UI and every data source, and add a domain layer only when business logic is reused or genuinely difficult to manage in a ViewModel. Keep durable state in models owned outside lifecycle-bound screens, expose one authoritative owner for each kind of data, and let events flow back to that owner. As the team and device matrix expand, split modules around cohesive features or domains rather than creating a maze of tiny projects.
This approach gives a product room to add features, survive process death and intermittent connectivity, support tablets and foldables, and let teams work independently without imposing structure that the app does not yet need.
The practical baseline: UI, data and an optional domain layer
Android’s official architecture guidance establishes a small baseline that works for both a new app and a product that is beginning to grow. The UI layer displays application data and handles user interaction. The data layer contains business logic and exposes application data. Repositories sit at the boundary, centralizing changes to data, hiding whether information came from a network, database or another source, and resolving conflicts when more than one source exists.
Quick wins for a faster PC:
Fix the driver behind crashes, sound loss and screen glitchesFind Drivers →Clear out junk files and repair common Windows errorsFree Scan →Scan for outdated or missing drivers - takes under a minuteDriver Scan →A domain layer can sit between UI and data, but it is not a mandatory tier. Introduce it when the same operation is used by several ViewModels or when presentation-facing business logic has become too complex to understand and test in one ViewModel. A small feature with one straightforward screen may be clearer without it.
#1 Best Overall
| Layer | Primary responsibility | What it should not do |
|---|---|---|
| UI | Render state, collect user input, and send events | Open databases, call network clients directly, or decide where data is stored |
| Domain (optional) | Represent reusable use cases and complex business rules | Own screen widgets or duplicate repository storage logic |
| Data | Implement repositories, data sources, mapping and business data operations | Know about a particular Activity, screen layout or navigation destination |
The most important boundary is between UI components and data sources. Even when an app has only one source, a repository keeps the source replaceable and gives the rest of the application one consistent API. Screens ask for application data; they do not need to know whether the answer came from a local store, a remote service or a combination of both.
How responsibilities and data flow fit together
Use a one-way path for normal data movement and a deliberate return path for user intent:
- The UI observes an immutable state model and renders it.
- A user action becomes an event, such as
Retry,SaveAddressorRefresh. - The ViewModel or another presentation coordinator handles the event and calls a repository or, where justified, a domain use case.
- The repository reads or changes the underlying sources, applies synchronization or conflict rules, and updates its owned model.
- The resulting state flows back through the ViewModel to the UI.
This arrangement makes the owner of a change visible. It also prevents two screens from quietly keeping incompatible copies of the same account, cart or settings data. A repository should expose data to callers in an immutable form; only the owner is allowed to mutate it.
When a domain layer earns its place
Do not create a use-case class for every function by default. Add the layer when at least one of these conditions is true:
- The same business operation is needed by multiple ViewModels or features.
- A ViewModel is coordinating several repositories, branching through many business rules, or becoming difficult to read.
- The operation has a clear business name that is more meaningful than a collection of low-level data calls.
If none applies, keeping the operation in the ViewModel or repository can reduce indirection. The goal is understandable responsibility, not a prescribed number of folders.
Design state for lifecycle loss and unreliable connections
Mobile operating systems control component lifecycles. Android can stop an app process when resources are needed, launch components individually or out of order, and destroy and recreate screens during configuration changes. An Activity or other lifecycle-bound entry point therefore cannot be the only place that important application data lives.
Keep durable state in models that outlive a screen. A process restart should reconstruct the UI from those models rather than leaving the app with an empty or contradictory screen. Persistent models are especially important for data that must remain available during an intermittent connection or after the user temporarily leaves the app.
Choose one owner for each data type
For every important data type, name a single source of truth: one component that can mutate the value and exposes it to the rest of the app in an immutable form. Examples include a repository owning the signed-in account, a local store owning a saved draft, or a synchronizing repository owning the latest known order.
| Data | Possible owner | UI interaction |
|---|---|---|
| Signed-in account | Account repository backed by the app’s account sources | Observe account state; send sign-out or refresh events |
| Checkout draft | Persistent draft model managed by a checkout repository | Render fields; send edits; restore after recreation |
| Temporary screen presentation | ViewModel state for that screen | Render loading, error and content states |
Two screens may display the same value, but they should not each become an independent writer. Central ownership avoids race conditions and makes refresh, logout and offline recovery predictable.
Use unidirectional data flow
Unidirectional data flow means state travels toward the UI and events travel back toward the state owner. Android’s current guidance pairs this model with lifecycle-aware state collection, ViewModels where appropriate, and coroutines and flows for communication between layers. These are Android recommendations; other mobile stacks may implement the same ideas with different primitives.
Represent the result of an event in state rather than relying on a fragile one-off signal. For example, a failed save can become an explicit SaveState.Error that remains available until the user edits the form or dismisses the message. The UI renders the state it receives, while the ViewModel coordinates events and delegates data changes.
A step-by-step build plan for a growing Android product
1. Map product responsibilities before choosing packages
List the user-visible capabilities and the data each one needs. Mark which information is durable, which is shared by several features, and which is only temporary presentation state. This map reveals natural boundaries before code structure makes them expensive to change.
2. Define models that can survive recreation
Create application-facing models for important concepts rather than exposing raw responses from a network client or database. Decide which models must be persisted and how they are rebuilt after process death. Keep screen state separate from durable business data so a loading indicator does not overwrite an order or draft.
3. Put every source behind a repository
Define repository interfaces around application needs, not around a vendor SDK. A repository can combine local and remote data, select the freshest value, queue a change, or report a conflict. Screens should depend on that interface and never reach around it to access a database or network client.
Rank #3
4. Add domain operations only for real reuse or complexity
If two ViewModels need the same eligibility rule, pricing calculation or synchronization operation, give that operation a domain-level home. If a single screen merely forwards a button tap to one repository method, an extra abstraction may make the code harder to follow.
Do these 3 things before closing this tab:
1Clear out junk files and repair common Windows errors2Fix the driver behind crashes, sound loss and screen glitches3Repair Windows errors before they cause bigger problems5. Expose immutable state from the presentation layer
Let a ViewModel collect repository flows, process events and publish a state model that the UI can observe with lifecycle awareness. Keep mutable implementation details private. When an event completes, update state and let the UI react to the new state instead of expecting the UI to manage a callback sequence.
6. Model loading, content and failure explicitly
For each screen, decide what the UI should display while data is loading, when cached data is available but a refresh fails, and when no data exists. A repository that can return a durable cached model gives the UI something useful to render during a network outage; an error state can then explain what action is available.
7. Make configuration changes ordinary, not exceptional
Rotation, window resizing and folding can recreate or rearrange UI components. Keep navigation decisions and important state outside the screen instance. The recreated UI should reconnect to the same state owner and render the current model without asking the user to repeat completed work.
8. Exercise process and connectivity scenarios
Walk through the cases that expose hidden ownership: leave a form, rotate the device, return after a process restart, disable the connection, retry a failed operation and open two screens that display the same record. If the result depends on a particular Activity instance or a second hidden copy of the data, move ownership to the appropriate model or repository.
Modularize when boundaries become useful
Android’s modularization guidance does not prescribe one project layout. The durable rule is high cohesion and low coupling: keep code that changes together together, and keep modules from knowing unnecessary details about one another. Two modules that depend heavily on each other may be a single boundary pretending to be two. Conversely, a module whose parts rarely interact may be ready to split.
| Module role | Useful contents | Good reason to create it |
|---|---|---|
| Data module | Repository implementations, data sources, mapping and domain data logic | A domain’s data needs should be encapsulated and consumed through a stable repository API |
| Feature module | Sign-up, checkout or another cohesive capability, including related UI and state handling | The feature has a clear boundary or needs to evolve with limited knowledge of other features |
| App module | Application entry point and commonly root navigation | The product needs one place to assemble features and start the app |
| Common module | Shared UI, analytics or networking used by multiple areas | The code is genuinely reused and the shared dependency will not become a dumping ground |
Signals that a split is justified
- A feature can be described and changed without understanding unrelated features.
- Several developers need to work in the area while minimizing cross-feature knowledge.
- A dependency is reused by multiple features and has a stable contract.
- Parts of the current module rarely interact and can communicate through a small interface.
Signals that a split is premature
- The proposed modules call each other for most operations.
- Moving a class would require exposing implementation details to several other modules.
- The split creates wrappers and interfaces without reducing conceptual coupling.
- The team cannot explain what independent responsibility each module owns.
When refactoring, extract one meaningful boundary at a time. First identify the owner and public contract, then move implementation behind that contract, and finally remove callers’ access to the old internals. A network of tiny modules can add build and coordination overhead without improving isolation.
Design for phones, tablets and changing windows
A product that grows in users may also grow across form factors. Android’s architecture guidance calls out tablets, foldables, ChromeOS, car displays and XR as possible targets. A fixed portrait phone layout is therefore an architectural constraint, not merely a visual choice.
| Change the app must handle | Architectural implication |
|---|---|
| Rotation or window resizing | Keep state outside the recreated UI and render from the current model |
| Fold and unfold | Allow navigation and layout decisions to respond to available space |
| Large screens | Use adaptive layouts that can show related content together when space permits |
| Car or XR surfaces | Keep presentation concerns separate from data and business rules so a different UI can consume the same state |
Do not let a particular screen width determine where data is stored or how a transaction works. The UI can choose a list, two-pane layout or alternate navigation arrangement while repositories and domain operations continue to expose the same application state.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
A decision framework for evaluating an architecture
When comparing a proposed structure with the current one, score it against the constraints that matter to the product rather than counting layers or patterns.
| Question | Healthy signal | Warning signal |
|---|---|---|
| Are responsibilities clear? | UI, data access and business rules can change independently | Screen classes contain storage, networking and business decisions together |
| Who owns each state? | One mutable owner exposes immutable data and survives recreation | Several screens maintain competing copies |
| Is a domain layer needed? | Logic is reused or too complex for a ViewModel | Every trivial function has an extra wrapper |
| Are modules cohesive? | Each module represents a meaningful feature or domain | Modules communicate through many internal details |
| Does it cover the device scope? | Layouts and navigation adapt to resizing and folding | Important state is tied to one portrait screen |
| Does it fit the team? | Boundaries let people work with limited cross-module knowledge | Build, dependency and coordination cost exceeds the isolation gained |
This framework also exposes trade-offs. A highly abstract design may improve reuse but slow a small team. A single module may be efficient early and become difficult later. Choose the smallest structure that makes the next expected change safe, then revisit the decision as evidence accumulates.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Common failure modes and how to correct them
Screen classes own durable data
Symptom: a rotation or process restart clears a draft, account or in-progress task. Correction: move the durable model to its repository or persistent store and have the recreated UI observe it.
UI calls a database or network client directly
Symptom: every screen implements its own loading, caching and error rules. Correction: place the source behind a repository and expose an application-level contract.
Crashes, No Sound, or Screen Glitches?
Random freezes, missing sound and display glitches usually trace back to one bad driver. Find and replace yours safely.Free scan · under a minutePC Slower Than It Used to Be?
A free scan shows the junk files, broken settings and background clutter dragging Windows down - then fixes them in one click.Free scan · Windows 10 & 11Two owners mutate the same record
Symptom: one screen shows a newer value while another silently overwrites it. Correction: select a single source of truth and make other consumers read immutable state.
One-off UI events carry business results
Symptom: a message, navigation action or success result disappears when the UI is recreated. Correction: represent the meaningful result in state and let the lifecycle-aware UI collect it.
A domain layer is added everywhere
Symptom: developers navigate through several classes to find a simple repository call. Correction: retain the layer only where logic is reused or genuinely complex.
Modules mirror folders instead of responsibilities
Symptom: modules depend on one another for nearly every change. Correction: merge tightly coupled pieces or redraw the boundary around a cohesive feature or domain.
Free tools Windows power users keep installed
One-click scans. No signup required.
Phone assumptions leak into architecture
Symptom: a fold, resize or large display loses context or forces a restart of a workflow. Correction: keep state and navigation independent of a fixed window size and use adaptive layouts.
Worked example: a checkout capability
Consider a product adding checkout to an existing app. The feature module can own checkout screens and their ViewModels. A checkout repository can expose the cart, address draft, available payment options and order status while hiding whether each value is local, remote or combined. A domain operation is justified if eligibility or order submission is reused by several screens or requires substantial rules.
The screen renders a state model such as editing, submitting, submitted or error. Tapping the submit button sends an event to the ViewModel. The ViewModel invokes the repository or domain operation; the repository becomes the sole mutator of the order state. If the process is killed during address entry, the persistent draft is restored when the feature starts again. If the window widens, the same state can drive a two-pane address-and-summary layout without changing checkout rules.
The app module assembles the feature and owns root navigation. Shared analytics or UI components belong in a common module only if multiple features genuinely consume them. Other features do not reach into checkout’s database tables or internal classes; they use the published contract.
Recommended Free Tools
Platform scope and what not to assume
The recommendations in this guide are grounded primarily in Android’s official architecture and modularization guidance. They describe Android layers, ViewModels, lifecycle-aware collection, coroutines and flows, and Android’s range of form factors. The available Apple material does not establish enough current implementation detail for a parallel iOS prescription, and the evidence does not support choosing a cross-platform framework. Teams targeting another platform should translate the principles—clear ownership, durable state, controlled dependencies and adaptive presentation—against that platform’s own guidance rather than assuming identical APIs or project layouts.
Architecture readiness checklist
- Every screen reads application state through a presentation layer rather than calling a data source directly.
- Each important data type has one documented mutable owner.
- Repositories exist even when a domain currently has one source.
- Durable models can reconstruct the UI after process death or a configuration change.
- Events update state through a traceable path instead of relying on hidden screen callbacks.
- A domain layer exists only where reuse or complexity warrants it.
- Modules represent cohesive features, data domains or truly shared capabilities.
- Module dependencies are intentionally one-way or otherwise easy to explain.
- Layouts and navigation respond to rotation, resizing and folding.
- The design has been checked against the team’s build, ownership and delivery constraints.
A durable default for growth
Start with the smallest architecture that protects the product’s future: UI over repositories and data sources, persistent models for important state, one owner per data type, and a one-way flow of state and events. Add domain and feature modules when reuse, complexity or team boundaries make them valuable. Keep the UI adaptive and the data contracts independent of a particular screen or device. This gives a growing Android product a structure that can absorb new features and form factors without turning every change into a rewrite.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

