android-modularization
Skill8 starsChanged 4 months agoApache-2.0View on GitHub
Designs and enforces a clean multi-module Android architecture with strict layer isolation. Use when user asks "where should this code go", "create a new module", "fix a circular dependency", "what layer does this belong in", or "set up module architecture".
1 other project uses a version of this.
Adopted by
Most recently taken up by
- krutikJain/android-agent-skillstheir own version, first seen Mar 25, 2026
Other versions1 project uses a version of this.
Word for word · 0
None found.
Edited · 0
None found.
Wrote their own version · 1
- krutikJain/android-agent-skills★ 14same name
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
In this repository14 other files it gives its agents.
- android-permissions-editorSkill
- android-unit-test-editorSkill
- compose-editorSkill
- compose-navigation3Skill
- compose-performance-auditorSkill
- github-action-editorSkill
- gradle-configurationSkill
- gradle-convention-pluginSkill
- koin-editorSkill
- kotlin-conventionSkill
- kotlin-coroutinesSkill
- mvi-editorSkill
- paparazzi-editorSkill
- review-owasp-securitySkill
The file
We split this skill into the rules it gives an agent
2 rules. About 0 are common, 0 are distinctive, and 2 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈0
Distinctive · estimate
This author's own choices. Worth a look.
2
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 226
All Dependency Injection configuration is centralized in the app module.
- Very specific: line 243
All navigation graphs live in app/navigation/.
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Android Modularization
Overview
This skill defines a four-layer module architecture for Android projects that scales well, enforces separation of concerns, and avoids circular dependencies. It is entirely project-agnostic and can be applied to any Android codebase.
Module Layers
app/
feature/
└── feature-a/
└── feature-b/
component/
└── component-x/
└── component-y/
integration/ ← optional, bridges components
└── integration-ab/
common/
└── common-utils/
└── common-ui/Layer Diagram
graph TD
subgraph APP["app (entry point)"]
A[":app"]
end
subgraph FEATURE["feature/* (presentation)"]
F1[":feature:feature-a"]
F2[":feature:feature-b"]
end
subgraph INTEGRATION["integration/* (optional bridge)"]
I1[":integration:a-b"]
end
subgraph COMPONENT["component/* (domain + data)"]
C1[":component:component-x"]
C2[":component:component-y"]
end
subgraph COMMON["common/* (shared utilities)"]
CM1[":common:utils"]
CM2[":common:ui"]
end
A --> F1
A --> F2
A --> C1
A --> C2
A --> I1
F1 --> C1
F1 --> CM1
F1 --> CM2
F2 --> C2
F2 --> I1
F2 --> CM2
I1 --> C1
I1 --> C2
I1 --> CM1
C1 --> CM1
C2 --> CM1
style APP fill:#4A90D9,color:#fff,stroke:#2c6fad
style FEATURE fill:#7B68EE,color:#fff,stroke:#5a4db0
style INTEGRATION fill:#E8A838,color:#fff,stroke:#b87d1a
style COMPONENT fill:#5BA85A,color:#fff,stroke:#3d7a3d
style COMMON fill:#888,color:#fff,stroke:#555Read the arrows as "depends on". Arrows always flow downward — never upward, never sideways within the same layer.
Layer Responsibilities
| Layer | Scope | Contains |
app | Entry point & wiring | DI initialization, Navigation graphs, Application class |
feature/* | UI presentation | Screens, ViewModels, MVI Contracts |
component/* | Business logic | Domain Models, Use Cases, Repository interfaces + implementations, Data Sources |
integration/* | Cross-component orchestration | Use Cases that coordinate ≥2 components |
common/* | Shared utilities | Extensions, formatters, shared UI primitives, no business logic |
Dependency Rules (Critical)
app → feature/*, component/*, integration/*, common/*
feature/* → component/*, integration/*, common/*
integration/* → component/*, common/*
component/* → common/* only
common/* → (no dependencies)Allowed vs Forbidden Dependencies
flowchart LR
subgraph ALLOWED["✅ Allowed"]
direction TB
a1["app → feature"]
a2["app → component"]
a3["feature → component"]
a4["feature → integration"]
a5["integration → component"]
a6["any → common"]
end
subgraph FORBIDDEN["❌ Forbidden"]
direction TB
f1["feature/A → feature/B"]
f2["component/A → component/B"]
f3["any → app"]
f4["common → feature/component"]
end
style ALLOWED fill:#d4edda,stroke:#28a745,color:#155724
style FORBIDDEN fill:#f8d7da,stroke:#dc3545,color:#721c24Zero-Tolerance Rules
- ❌
feature/A→feature/B— Features must never depend on each other - ❌
component/A→component/B— Components must never depend on each other - ❌ Any module →
app—appis a leaf consumer - ✅ When
component/Aneedscomponent/B, createintegration/A-B
Why These Rules?
- Prevents circular dependencies: A → B → A impossible
- Enables independent testing: Each module testable in isolation
- Parallel development: Teams can work on separate features simultaneously
- Easy replacement: Swap implementations without breaking consumers
Module Structure by Type
Feature Module
feature/my-feature/
├── build.gradle.kts
└── src/
└── main/java/.../feature/myfeature/
├── MyFeatureContract.kt # ViewState / ViewEvent / SideEffect
├── MyFeatureViewModel.kt
├── MyFeatureScreen.kt
└── components/ # Internal composablesbuild.gradle.kts (minimal):
plugins {
id("android-library-convention") // your convention plugin
id("compose-convention")
id("unit-test-convention")
}
dependencies {
implementation(project(":component:my-component"))
implementation(project(":common:ui"))
}Component Module
component/my-component/
├── build.gradle.kts
└── src/
└── main/java/.../component/mycomponent/
├── domain/
│ ├── model/ # Domain data classes
│ ├── repository/ # Repository interfaces
│ └── usecase/ # Use Cases
└── data/
├── repository/ # Repository implementations
├── datasource/ # Local / Remote data sources
└── mapper/ # Entity ↔ Domain mappersbuild.gradle.kts (minimal):
plugins {
id("android-library-convention")
id("unit-test-convention")
}
dependencies {
implementation(project(":common:utils"))
}Common Module
common/utils/
├── build.gradle.kts
└── src/
└── main/java/.../common/utils/
├── extensions/
└── formatters/Common modules have zero dependencies on other project modules.
DI Wiring — Always in app
All Dependency Injection configuration is centralized in the app module.
app/src/main/java/.../di/
├── AppModule.kt # Platform & framework providers
├── RepositoryModule.kt # Data layer bindings
├── DomainModule.kt # Use Cases
└── ViewModelModule.kt # ViewModels- Feature and component modules use constructor injection only
- They never contain DI setup code
Navigation — Always in app
All navigation graphs live in app/navigation/.
app/src/main/java/.../navigation/
├── RootNavGraph.kt
└── myfeature/
└── MyFeatureNavigation.kt- Feature modules expose screen composables only
- Navigation side effects are handled at the
applevel
When to Create an Integration Module
Create integration/A-B when:
- A Use Case from
component/Aneeds data fromcomponent/B - An orchestration flow spans multiple components
- You find yourself wanting to add
component/Bas a dependency ofcomponent/A
// integration/order-payment/domain/usecase/PlaceOrderUseCase.kt
class PlaceOrderUseCase(
private val orderRepository: OrderRepository, // from component/order
private val paymentRepository: PaymentRepository, // from component/payment
)Common Anti-Patterns
❌ Business logic in feature modules
// BAD — logic inside ViewModel
class OrderViewModel : ViewModel() {
fun calculateTotal(items: List<Item>): Double {
return items.sumOf { it.price * it.quantity } * 1.21 // VAT hardcoded
}
}// GOOD — logic in Use Case inside component
class CalculateOrderTotalUseCase(private val taxRate: Double) {
operator fun invoke(items: List<Item>): Double =
items.sumOf { it.price * it.quantity } * (1 + taxRate)
}❌ Shared state between features via direct dependency
// BAD
// feature/checkout depends on feature/cart
implementation(project(":feature:cart"))// GOOD — both features depend on component/cart
// feature/checkout → component/cart
// feature/cart → component/cart❌ Repository implementation in domain layer
// BAD — RepositoryImpl inside domain/
component/orders/domain/repository/OrderRepositoryImpl.kt// GOOD — interface in domain, impl in data
component/orders/domain/repository/OrderRepository.kt // interface
component/orders/data/repository/OrderRepositoryImpl.kt // implementationChecklist: New Module
- Module placed in correct layer (
feature/,component/,common/,integration/) - Only depends on layers below it (see dependency table)
- No dependency on
appmodule - Business logic NOT placed in
featuremodule - UI NOT placed in
componentmodule - DI registration added in
app/di/ - Navigation (if UI module) added in
app/navigation/ - Convention plugin applied (no manual SDK/test config)
References
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

