Full Architecture + System Design Mock Pack
Complete architecture and system design mock round with real-world scenarios and self-scoring rubric
Open interactive version (quiz + challenge)Real-world analogy
What is it?
A full architecture and system design mock pack for senior Android engineers. Covers Clean Architecture deep-dives, offline-first system design scenarios, multi-module architecture, and testing strategy design. Includes model answers with real-world nuance, explicit trade-off articulation, and a 1–4 self-scoring rubric calibrated to the senior bar at product companies and scale-ups.
Real-world relevance
At Klarna, Wise, or Grab, a senior Android architecture round typically begins: 'Design the notification delivery system for our mobile app — users must receive payment confirmations reliably, even with intermittent connectivity.' A senior candidate will immediately ask: What is our current DAU? Is the notification user-initiated or server-pushed? Do we need deduplication? What is the acceptable delivery latency? This scoping behavior alone signals senior-level thinking before the design has started.
Key points
- Architecture mock format — Senior architecture rounds are 45–60 minutes. Expect: one large system design question (20–25 mins), one architecture deep-dive on a past project (15–20 mins), and follow-up questions on trade-offs. This mock simulates all three formats.
- System design question formula — For any system design question: (1) Clarify scope and scale, (2) Define components, (3) Describe data flow, (4) Address offline/failure scenarios, (5) Discuss testing strategy, (6) Name the trade-offs of your choices. Never skip step 1 — clarifying scope separates senior from junior.
- Clean Architecture mock — Key questions: Why is the domain layer Android-independent? What does a use case return — raw data or Result? How do you handle mapping between layers? What goes in the data layer vs domain layer? What is the difference between a repository and a data source?
- Offline-first sync design mock — Key scenario: Design offline-first payment history for a fintech app with 1M users, conflict resolution, and background sync. Expect follow-ups on: sync frequency, conflict strategy (client-wins vs server-wins vs merge), deletion handling, battery and bandwidth optimization.
- Multi-module architecture mock — Key questions: Why modularize? How do you prevent feature modules from depending on each other? What lives in :core vs :feature vs :app modules? How does dependency injection cross module boundaries? How does modularization affect build time?
- Testing strategy mock — Key question: Design the testing strategy for a new fintech feature end-to-end. Expect: test pyramid (unit vs integration vs E2E ratios), what to mock vs what to use real implementations for, testing network layer with OkHttp MockWebServer, testing Room with in-memory database.
- Trade-off articulation — Every architecture decision must come with explicit trade-offs. 'I chose Room over SQLDelight because our team knows Room well, though SQLDelight offers compile-time SQL verification and multiplatform support.' Interviewers score your reasoning, not just your choice.
- Real project references — In an architecture round, reference your real projects explicitly. 'On the Payless POS project, I used an event-sourcing approach for the transaction log because...' is infinitely more credible than 'in theory, you would use...'
- Failure and recovery design — Senior architects design for failure. Questions include: What happens when the sync worker fails repeatedly? How do you retry with exponential backoff? How do you surface sync errors to the user without blocking them? How do you detect and resolve data corruption?
- Scalability signals — Show scalability thinking: 'This works for 10k users. At 1M users the local Room database would be fine, but the API pagination strategy would need cursor-based pagination rather than offset-based to avoid N+1 query problems at the server side.'
- Self-scoring architecture rubric — 1 = Cannot describe the layers or their responsibilities. 2 = Describes architecture but cannot articulate why each boundary exists. 3 = Correct structure, clear trade-offs, references real patterns. 4 = Correct structure, compelling trade-offs, real project evidence, proactively identifies failure modes and edge cases.
- Capstone synthesis — At the end of this mock, synthesize: which architectural decisions across your career are you most proud of? Which ones would you make differently? This synthesis question appears in final-round interviews to distinguish candidates who reflect and grow from those who just ship.
Code example
// ARCHITECTURE MOCK — SYSTEM DESIGN SCENARIOS
// ============================================================
// SCENARIO 1: OFFLINE-FIRST PAYMENT HISTORY
// "Design an offline-first payment history feature for 1M users"
// Score yourself /4 before reading the model answer
// ============================================================
/*
STEP 1 — CLARIFY (always first):
- How many transactions per user on average? (1–50/day)
- What is the freshness requirement? (near-real-time vs daily sync)
- Conflict resolution: who wins — client or server?
- What happens on deletion? Soft delete or hard delete?
STEP 2 — COMPONENTS:
UI layer: TransactionHistoryScreen (Compose)
-> collects from ViewModel.transactionsFlow
ViewModel: TransactionViewModel
-> exposes StateFlow<List<Transaction>> from UseCase
Domain: GetTransactionsUseCase
-> calls TransactionRepository.observeTransactions()
Data: TransactionRepository
-> reads from Room (SSOT)
-> WorkManager triggers SyncWorker on connectivity change
SyncWorker -> calls API -> writes to Room -> Room emits to Flow -> UI updates
STEP 3 — CONFLICT RESOLUTION:
Strategy: server-wins (simpler, consistent, appropriate for financial data)
Each record has: id, serverId, lastModifiedAt (server timestamp)
SyncWorker compares local lastModifiedAt vs server payload
Server version replaces local if server is newer
STEP 4 — DELETION:
Soft delete: isDeleted = true, deletedAt timestamp
SyncWorker processes deletions after upserts to avoid missed deletes
Hard delete from Room only after server confirms deletion
STEP 5 — TRADE-OFFS STATED:
"I chose server-wins over merge because financial data must be authoritative.
Client-wins would risk showing stale balances. The trade-off is that
optimistic UI updates for new transactions must be rolled back on sync conflict
— I handle this with a 'pending' state on local inserts."
*/
// ============================================================
// SCENARIO 2: MULTI-MODULE BUILD ARCHITECTURE
// "How would you structure a 20-engineer Android app into modules?"
// ============================================================
/*
Module hierarchy (dependency flows downward):
:app (assembles, no business logic)
|
:feature:payments
:feature:profile
:feature:notifications
|
:domain (use cases, entities — pure Kotlin, no Android)
|
:data (repositories, API, Room — implements domain interfaces)
|
:core:ui (design system, shared Compose components)
:core:network (OkHttp, Retrofit setup)
:core:testing (shared fakes, test utilities)
Rules:
- :feature modules CANNOT depend on each other
- :feature modules depend on :domain, NOT :data
- :data depends on :domain (implements interfaces)
- Navigation between features: use a navigation contract in :domain
DI across modules: each module exposes a Hilt @Module
that binds its implementations. :app installs all modules.
Build time impact:
- Modules enable parallel compilation
- Modules enable build cache — unchanged modules reuse cached output
- Trade-off: more Gradle config complexity, need strict dependency rules
*/
// SELF-SCORE EACH SCENARIO /4 then compare with model answers aboveLine-by-line walkthrough
- 1. Step 1 of every system design question: Clarify scope and scale. This is the most visible senior signal in an architecture interview.
- 2. Offline-first pattern: Room is SSOT. Network data flows API -> Room -> Flow -> UI. UI never calls the API directly.
- 3. Conflict resolution for financial data: server-wins. Document this choice and the trade-off (optimistic UI requires rollback on conflict).
- 4. Soft delete: isDeleted flag + deletedAt timestamp prevents sync races when hard-deleting records before sync propagates.
- 5. Multi-module hierarchy: app -> feature -> domain <- data. Feature modules must not depend on each other.
- 6. Domain layer purity: no Android imports in domain. This enables fast JVM unit tests without Robolectric or device.
- 7. DI across modules: each module exposes a Hilt @Module. :app installs all modules as the composition root.
- 8. Build time improvement: parallel compilation of independent modules + build cache for unchanged modules.
- 9. Trade-off articulation: every architectural choice must include explicit trade-offs. This is the senior signal interviewers score highest.
- 10. Real project references: 'On [your project], I used X because Y' is more credible than theoretical answers.
- 11. Scalability signals: state what changes at 10x scale. Cursor-based pagination vs offset, database sharding, CDN for assets.
- 12. Synthesis question: prepare your answer to 'what architectural decision are you most proud of and what would you change?' — this is a common final-round question.
Spot the bug
// ARCHITECTURE DESIGN BUG — FIND THE VIOLATIONS:
// Feature module: feature-payments
class PaymentViewModel @Inject constructor(
private val userRepository: UserRepository,
private val paymentRepository: PaymentRepository,
private val analyticsTracker: FirebaseAnalyticsTracker
) : ViewModel() {
fun processPayment(amount: Double) {
viewModelScope.launch {
val user = userRepository.getCurrentUser()
val result = paymentRepository.pay(amount, user.id)
analyticsTracker.logEvent("payment_complete", mapOf("amount" to amount))
_uiState.value = UiState.Success(result)
}
}
}
// domain/UserRepository.kt
interface UserRepository {
suspend fun getCurrentUser(): User
}
// data/UserRepositoryImpl.kt
class UserRepositoryImpl @Inject constructor(
private val db: AppDatabase, // Room database
private val context: Context // Android Context
) : UserRepository { ... }Need a hint?
Show answer
Explain like I'm 5
Fun fact
Hands-on challenge
More resources
- Android Architecture — Official Guide (Android Developers)
- Now in Android — Reference App Architecture (Android / GitHub)
- WorkManager — Background Sync Guide (Android Developers)
- Room Persistence Library (Android Developers)
- Designing Data-Intensive Applications — Martin Kleppmann (O'Reilly)