Hey! Let's learn this together. I'll explain everything like you've never coded an Android app before. No jargon without explanation. Lots of pictures. Promise.
Chapter 1: What is "Architecture"? (The House Story)
Imagine you want to build a house.
You have two choices:
flowchart LR
subgraph Bad[Way 1: No Plan]
B1[Throw bricks
wherever]
B2[Kitchen in bedroom]
B3[Toilet in living room]
B4[House falls down]
end
subgraph Good[Way 2: With a Plan]
G1[Foundation first]
G2[Walls next]
G3[Rooms organized]
G4[Strong house]
end
Architecture in Android = the plan for your app.
It tells you:
- Where to put what code
- How different parts talk to each other
- How to change things later without breaking everything
That's it. No magic. Just organization.
Chapter 2: The Restaurant Story (Understanding the Layers)
Forget code for a moment. Imagine a restaurant.
flowchart TB
Customer([Customer
You]) -->|orders food| Waiter
Waiter -->|tells the order| Chef
Chef -->|asks for ingredients| Storage[Storage Room
Fridge + Pantry]
Storage -->|gives ingredients| Chef
Chef -->|cooks and gives food| Waiter
Waiter -->|serves| Customer
Who does what?
| Person | Job | Does NOT do |
|---|---|---|
| Customer | Orders, eats | Cook, manage storage |
| Waiter | Takes order, serves | Cook, store food |
| Chef | Cooks | Greet customer, manage storage |
| Storage | Holds ingredients | Cook, talk to customer |
This is EXACTLY how Android apps are organized.
flowchart TB
User([User
Customer]) -->|taps button| UI[UI
Waiter]
UI -->|asks for data| VM[ViewModel
Chef]
VM -->|gets data| Repo[Repository
Storage Room]
Repo -->|returns data| VM
VM -->|gives ready data| UI
UI -->|shows on screen| User
| Restaurant | Android |
|---|---|
| Customer | User |
| Waiter | UI (Buttons, Screens) |
| Chef | ViewModel |
| Storage | Repository (Database + Internet) |
One golden rule: The waiter doesn't cook. The chef doesn't serve. Each part has ONE job.
Chapter 3: What Happens When You Tap a Button?
Let's say you open Instagram and tap the heart icon to like a photo.
sequenceDiagram
actor You
participant Screen as Screen
(UI)
participant Brain as ViewModel
(Brain)
participant Storage as Repository
(Storage)
participant Internet as Internet
(Server)
You->>Screen: Tap heart
Screen->>Brain: User tapped like!
Brain->>Storage: Save the like
Storage->>Internet: Tell server
Internet-->>Storage: Done!
Storage-->>Brain: Saved!
Brain-->>Screen: Show red heart
Screen-->>You: red heart
Read it like a story:
- You tap the heart
- Screen tells the Brain: "Hey, user wants to like this!"
- Brain tells Storage: "Please save this like"
- Storage tells Internet: "Send this to server"
- Internet says: "Done!"
- Storage tells Brain: "All saved!"
- Brain tells Screen: "Make the heart red"
- You see the red heart
That's an entire architecture working. Just a chain of helpers, each doing one small job.
Chapter 4: Your First Real Example (Counter App)
Let's build the simplest app ever: a button that counts how many times you tap it.
The 3 Pieces
flowchart LR
subgraph Screen[1. Screen - What You See]
Btn[Button: Tap me]
Num[Number: 0]
end
subgraph Brain[2. Brain - ViewModel]
Counter[count = 0]
Func[increment]
end
Btn -->|tap!| Func
Func -->|count + 1| Counter
Counter -->|new number| Num
The Code (Don't worry, I'll explain every line)
Step 1: The Brain (ViewModel)
class CounterViewModel : ViewModel() {
// This holds the number. It starts at 0.
private val _count = MutableStateFlow(0)
// The screen will watch this.
val count: StateFlow<Int> = _count
// When user taps the button, this runs.
fun onTapClick() {
_count.value = _count.value + 1
}
}
What's happening?
ViewModel= the brain. It thinks. It remembers._count= a box that holds the number 0.MutableStateFlow= a magic box that tells anyone watching when the number changes.onTapClick()= the function that runs when you tap.
Step 2: The Screen (Composable)
@Composable
fun CounterScreen(viewModel: CounterViewModel = viewModel()) {
// Watch the count. Whenever it changes, redraw the screen.
val count by viewModel.count.collectAsState()
Column {
Text("You tapped $count times")
Button(onClick = { viewModel.onTapClick() }) {
Text("Tap me!")
}
}
}
What's happening?
@Composable= "this is a piece of UI"collectAsState()= "watch the brain's number"- When user taps → call brain's function → brain updates number → screen sees new number → screen redraws
The Flow Animation
flowchart TB
A[User sees: 0] -->|tap| B[Screen says: onTapClick!]
B --> C[Brain: count = 0 + 1 = 1]
C --> D[Brain shouts: New value is 1!]
D --> E[Screen hears: redraw with 1]
E --> F[User sees: 1]
F -->|tap again| G[count = 2]
G --> H[User sees: 2]
That's MVVM. Yes, you just learned MVVM.
Chapter 5: What Do Those Scary Words Mean?
Now let's name the things you saw:
flowchart TB
subgraph MVVM
M[M = Model
The data
like: count = 5]
V[V = View
What user sees
Buttons, Text]
VM[VM = ViewModel
The brain in middle]
end
V -->|user actions| VM
VM -->|data| V
VM <--> M
| Word | Means | Like in restaurant |
|---|---|---|
| Model | The actual data | The food itself |
| View | What user sees | The waiter showing the food |
| ViewModel | The brain connecting them | The chef |
| MVVM | Model + View + ViewModel together | The whole restaurant system |
| Compose | New way to draw screens in Android | Modern way to design plates |
| StateFlow | Magic box that shouts when changed | Bell on the counter |
| Kotlin | Programming language we use | The language everyone speaks |
Chapter 6: Where Does Data Come From? (The Repository)
Apps need data. From where?
flowchart TB
subgraph Sources[Where data lives]
Internet[Internet
Server somewhere]
Phone[Your Phone
Local Database]
Memory[RAM
Just for now]
end
Sources --> Repo[Repository
The Manager]
Repo --> Brain[ViewModel]
Brain --> Screen[Screen]
Repository = one place that knows where to get data.
Without Repository:
- ViewModel has to know about internet
- ViewModel has to know about database
- ViewModel has to know about everything
- ViewModel becomes a monster
With Repository:
- ViewModel just asks Repository: "Give me data"
- Repository decides: "Should I get from internet or phone?"
- ViewModel doesn't care. Simple.
Example
class UserRepository {
// Get user. First try phone. If not there, get from internet.
suspend fun getUser(id: String): User {
val userInPhone = database.findUser(id)
if (userInPhone != null) {
return userInPhone // Found in phone, easy!
}
// Not in phone, ask the internet
val userFromInternet = api.fetchUser(id)
database.save(userFromInternet) // Save for next time
return userFromInternet
}
}
Now ViewModel just does:
class ProfileViewModel(private val repo: UserRepository) : ViewModel() {
fun loadUser() {
viewModelScope.launch {
val user = repo.getUser("user123")
// Show on screen
}
}
}
ViewModel has NO IDEA whether the data came from internet or database. And it doesn't care! That's the beauty.
Chapter 7: Clean Architecture (The Building with Floors)
Imagine a 3-floor building:
flowchart TB
subgraph Floor3[Floor 3: Presentation - What User Sees]
UI[Screens + ViewModels]
end
subgraph Floor2[Floor 2: Domain - The Rules]
UseCases[Use Cases
Business Rules]
end
subgraph Floor1[Floor 1: Data - Where Data Lives]
Data[Repository + API + Database]
end
Floor3 -->|asks| Floor2
Floor2 -->|asks| Floor1
Floor1 -->|gives data back| Floor2
Floor2 -->|gives data back| Floor3
What's a "Use Case"?
A Use Case is ONE thing your app can do.
Examples:
- LoginUseCase → "Log the user in"
- GetUserUseCase → "Get user info"
- LikePhotoUseCase → "Like a photo"
- LogoutUseCase → "Log the user out"
Each Use Case does ONE job. That's it.
class LoginUseCase(private val repo: AuthRepository) {
// This is the only thing this class does. Login. Nothing else.
suspend operator fun invoke(email: String, password: String): Result<User> {
return repo.login(email, password)
}
}
Why have Use Cases?
flowchart LR
subgraph Without[Without Use Cases]
VM1[ViewModel knows
about Repository
+ all business rules
+ validation
= BIG]
end
subgraph With[With Use Cases]
VM2[ViewModel] --> UC1[LoginUseCase]
VM2 --> UC2[ValidateEmailUseCase]
UC1 --> Repo[Repository]
end
Without: ViewModel does everything = big mess.
With: ViewModel just calls small Use Cases = neat and tidy.
Chapter 8: Hilt — The Magic Connector
Look at this code:
class LoginViewModel(
private val loginUseCase: LoginUseCase // where does this come from?
)
loginUseCase needs a Repository. Repository needs an API and a Database. API needs Retrofit. Retrofit needs OkHttp.
Without Hilt:
val okHttp = OkHttp()
val retrofit = Retrofit(okHttp)
val api = Api(retrofit)
val database = Database()
val repo = Repository(api, database)
val useCase = LoginUseCase(repo)
val viewModel = LoginViewModel(useCase)
You have to write ALL of this YOURSELF. For EVERY screen.
With Hilt:
@HiltViewModel
class LoginViewModel @Inject constructor(
private val loginUseCase: LoginUseCase // Hilt builds it!
) : ViewModel()
That's it. Hilt does all the wiring for you.
The Hilt Story
flowchart TB
Hilt[Hilt - The Genie] -->|I need a ViewModel!| Build[Hilt builds it...]
Build --> Step1[Build OkHttp]
Build --> Step2[Build Retrofit]
Build --> Step3[Build API]
Build --> Step4[Build Database]
Build --> Step5[Build Repository]
Build --> Step6[Build UseCase]
Build --> Step7[Build ViewModel]
Step7 --> Done[Here's your ViewModel!]
You tell Hilt once how to build each thing. Then it builds everything automatically forever.
@Module
@InstallIn(SingletonComponent::class)
object AppModule {
@Provides
fun provideApi(): Api = Retrofit.Builder()
.baseUrl("https://api.example.com")
.build()
.create(Api::class.java)
@Provides
fun provideDatabase(@ApplicationContext ctx: Context): Database {
return Room.databaseBuilder(ctx, Database::class.java, "app.db").build()
}
}
After this, Hilt knows how to make an Api and a Database. Anywhere you need them, just write @Inject.
Chapter 9: Modularization (LEGO Blocks)
Imagine your whole app is in ONE giant file. 100,000 lines.
Problems:
- Change one thing → rebuild EVERYTHING
- Two developers → both editing same file → conflicts
- Can't reuse code in another app
Solution: Break into LEGO blocks (modules)
flowchart TB
APP[:app
Main app]
subgraph Features[Feature LEGOs]
F1[:feature:login]
F2[:feature:home]
F3[:feature:profile]
end
subgraph Tools[Shared Tool LEGOs]
T1[:core:network]
T2[:core:database]
T3[:core:ui]
end
APP --> F1
APP --> F2
APP --> F3
F1 --> T1
F2 --> T1
F3 --> T2
F1 --> T3
F2 --> T3
F3 --> T3
Why is this awesome?
flowchart LR
Edit[You edit :feature:login] --> Rebuild[Only rebuild :feature:login + :app]
Edit2[Other modules?] --> Cached[Cached! Fast!]
Build time goes from 5 minutes → 30 seconds.
Folder structure
MyApp/
├── app/ ← entry point
│
├── feature/
│ ├── login/ ← LEGO: login screen
│ ├── home/ ← LEGO: home screen
│ └── profile/ ← LEGO: profile screen
│
└── core/
├── network/ ← LEGO: Retrofit stuff
├── database/ ← LEGO: Room stuff
├── ui/ ← LEGO: shared buttons/colors
└── domain/ ← LEGO: Use Cases + entities
Each folder is independent. Like a LEGO block. You can take any block out and the rest still works (mostly).
Chapter 10: The Complete Picture
Now let's put it ALL together. Here's a complete app's architecture:
flowchart TB
User([User])
subgraph UI[1. UI Layer - What User Sees]
Screen[Compose Screen]
VM[ViewModel
holds UI state]
end
subgraph Domain[2. Domain Layer - Business Rules]
UC[Use Cases
One job each]
end
subgraph Data[3. Data Layer - Where Data Lives]
Repo[Repository]
API[Remote API]
DB[Local Database]
end
User -->|taps| Screen
Screen -->|action| VM
VM -->|call| UC
UC -->|ask data| Repo
Repo --> API
Repo --> DB
DB -->|data| Repo
API -->|data| Repo
Repo -->|data| UC
UC -->|data| VM
VM -->|state| Screen
Screen -->|shows| User
Chapter 11: Full Animation — Login Example
You're going to log into an app. Watch what happens behind the scenes:
sequenceDiagram
autonumber
actor User
participant Screen as Login Screen
participant VM as LoginViewModel
participant UC as LoginUseCase
participant Repo as AuthRepository
participant API as Server
participant DB as Database
User->>Screen: Types email + password
User->>Screen: Taps "Login"
Screen->>VM: onLoginClick()
VM->>VM: Show loading spinner
VM->>UC: invoke(email, password)
UC->>Repo: login(email, password)
Repo->>API: POST /login
API-->>Repo: Success + token
Repo->>DB: Save user info
DB-->>Repo: Saved
Repo-->>UC: Result.success(User)
UC-->>VM: Result.success(User)
VM->>VM: Hide spinner, success!
VM-->>Screen: Navigate to Home
Screen-->>User: Welcome home!
Read it like a story. Each arrow is one step. Each character has one job.
Chapter 12: Common Mistakes Beginners Make
flowchart TB
subgraph Bad[Don't Do This]
B1[Put everything in Activity]
B2[Network call from Composable]
B3[Database access from ViewModel directly]
B4[Reference Activity in ViewModel]
end
subgraph Good[Do This]
G1[Composable to ViewModel to Repository]
G2[Repository handles network + DB]
G3[Use Hilt for dependencies]
G4[Keep ViewModel free of Android stuff]
end
| Mistake | Better way |
|---|---|
Putting network code in Activity | Put it in Repository |
| Calling API directly from screen | Screen → ViewModel → UseCase → Repository |
Holding Context in ViewModel (memory leak!) | Pass only data, no Context |
Logic inside @Composable | Logic in ViewModel |
| Mixing UI + business rules | Keep them in separate layers |
Chapter 13: Your Learning Roadmap
flowchart LR
Step1[1. Learn Kotlin basics] --> Step2[2. Build a Counter app]
Step2 --> Step3[3. Add ViewModel + StateFlow]
Step3 --> Step4[4. Add Repository]
Step4 --> Step5[5. Add Retrofit + Room]
Step5 --> Step6[6. Add Hilt]
Step6 --> Step7[7. Add Use Cases]
Step7 --> Step8[8. Split into modules]
Step8 --> Pro[You're a pro!]
Pace yourself. Don't try to learn everything in one day. Build small. Each step takes 1-2 weeks.
Chapter 14: Cheat Sheet (Print This)
| Question | Answer |
|---|---|
| Where does UI go? | Composable functions |
| Where does state go? | ViewModel (in a StateFlow) |
| Where does business logic go? | UseCase classes |
| Where does data fetching go? | Repository |
| How do I connect everything? | Hilt with @Inject |
| How do I update UI? | Update StateFlow in ViewModel |
| What survives screen rotation? | ViewModel |
| What should NOT be in ViewModel? | Anything Android-specific (Context, View) |
Chapter 15: Real Talk
You don't need ALL of this from day 1.
Day 1: Just Composables + simple ViewModel + StateFlow.
Day 30: Add Repository + Retrofit + Room.
Day 90: Add Hilt + Use Cases.
Day 180: Modularize.
flowchart LR
Today[Today: You
Knows nothing] --> M1[Month 1
MVVM]
M1 --> M3[Month 3
Repository]
M3 --> M6[Month 6
Clean Arch]
M6 --> Y1[1 Year
Modular]
Y1 --> Pro[Senior Android Dev]
Every senior developer was a beginner once. Take it slow. Build things. Break things. Learn.
Final Words
Architecture is just organization. Don't be scared of fancy names like "MVVM" or "Clean Architecture." They all mean the same thing: separate your code so each part has one job.
Start small. Build a Counter. Then a Todo. Then a News app. Each project teaches you one new piece.
Ask for help. Stack Overflow, Reddit r/androiddev, the Android docs. Everyone was confused once.
You got this!
Want more like this?
Check out my interview prep with 193+ questions covering Java, Kotlin, Android, DSA & Architecture — with English & Hindi answers.