agentsclimarketplace

Project architecture

Skill thetruong1099/android-mvi-base-code/.claude/skills/project-architecture

Module structure, dependency rules, and naming conventions for the Android MVI Template. Use when creating new feature or domain modules, understanding which modules can depend on which, choosing convention plugins for build.gradle.kts, following naming conventions for ViewModels/UseCases/Repositories/Screens, or adding screens to the navigation graph.From its SKILL.md

Install
npx -y skills add thetruong1099/android-mvi-base-code --skill project-architecture

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

2 things to look at

  • no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.
  • 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

SKILL.md

7.0 KB, ~1.5k tokens by cl100k_base, as published. Nobody here has run it

Project Architecture

Module Dependency Graph

app
 ├── feature:navigation-graph
 │    ├── feature:sample
 │    └── feature:core (shared by all features)
 ├── domain:usecase
 │    ├── domain:repository (interfaces only)
 │    ├── domain:model (pure data classes)
 │    └── domain:core (BaseUseCase, DataState, AppError)
 └── data:remote-data / data:local-data
      └── data:core (BaseDataSource, BaseMapper, ExceptionMapper)
           └── domain:model

Strict Dependency Rules

  1. Feature modules depend on: feature:core, domain:model, domain:usecase
  2. Feature modules NEVER depend on: data:*, other feature:* modules
  3. Domain modules are 100% pure Kotlin (NO android.* imports)
  4. Data modules depend on: domain:repository, domain:model, domain:core, data:core
  5. feature:core is the ONLY feature module that provides shared base classes

Convention Plugins (build_logic/)

Plugin IDPurposeApply to
android.applicationApp module config:app
android.application.composeCompose for app:app
android.featureBase feature modulefeature:*
android.feature.composeFeature + Compose + Hiltfeature:*
android.libraryLibrary moduledomain:*, data:*
android.hiltHilt DI setupmodules needing DI
android.firebaseFirebase configmodules using Firebase
android.test.unitJUnit + MockK + Coroutine testall modules
android.test.instrumentationAndroidJUnit4 + Compose testfeature modules
android.test.robolectricRobolectric setupdomain/data modules

Creating a New Feature Module

  1. Copy feature/sample/ as a template
  2. Add to settings.gradle.kts: include(":feature:new-feature")
  3. Create build.gradle.kts:
plugins {
    alias(libs.plugins.android.feature.compose)
    // alias(libs.plugins.android.test.unit)  // Add if tests needed
}

dependencies {
    implementation(projects.domain.model)
    implementation(projects.domain.usecase)
}
  1. Create files: XxxViewModel.kt, XxxScreen.kt, XxxNavigation.kt
  2. Register NavGraphBuilder.xxxScreen() in feature:navigation-graph
  3. Add implementation(projects.feature.newFeature) in app/build.gradle.kts

Creating a New UseCase

domain/usecase/src/main/java/com/template/domain/
├── usecase/newfeature/
│   ├── GetNewFeatureUseCase.kt       (interface)
│   └── GetNewFeatureUseCaseImpl.kt   (implementation)
└── di/
    └── NewFeatureUseCaseModule.kt    (Hilt @Module with @Binds)
// Interface - extends appropriate base
interface GetXxxUseCase : BaseFlowUseCaseNoParams<Flow<PagingData<Xxx>>>

// Implementation - @Inject constructor
class GetXxxUseCaseImpl @Inject constructor(
    private val repository: XxxRepository
) : GetXxxUseCase {
    override fun invoke(): Flow<PagingData<Xxx>> = repository.getXxx()
}

// DI Module - @Binds interface -> impl
@Module
@InstallIn(SingletonComponent::class)
interface XxxUseCaseModule {
    @Binds fun bindGetXxxUseCase(impl: GetXxxUseCaseImpl): GetXxxUseCase
}

UseCase Base Types

TypeWhen to use
BaseSuspendUseCase<T: IParams, R>One-shot with params
BaseSuspendUseCaseNoParams<R>One-shot without params
BaseFlowUseCase<T: IParams, R>Streaming with params
BaseFlowUseCaseNoParams<R>Streaming without params

Naming Conventions

TypeConventionExample
ViewModelXxxViewModelSampleViewModel
StateXxxViewStateSampleViewState
EventXxxViewEventSampleViewEvent
EffectXxxViewEffectSampleViewEffect
ScreenXxxScreen / XxxScreenInternalSampleScreen
NavigationXxxNavigation.kt + NavGraphBuilder.xxxScreen()sampleScreen()
UseCaseVerbNounUseCase + VerbNounUseCaseImplGetSampleItemsUseCase
MapperXxxMapperItemMapper
RepositoryXxxRepository (interface) + XxxRepositoryImplItemRepository

Hilt DI Organization

domain/usecase/di/         -> UseCaseModules (@Binds interface -> impl)
data/remote-data/di/       -> RepositoryModules, DataSourceModules, MapperModules
data/local-data/di/        -> DatabaseModule, DaoModule, DataStoreModule
app/di/                    -> AppModule (singletons, app-level bindings)

Data Layer Strategy Pattern

BaseDataSource
├── fetchData()         -> FirebaseDataSourceStrategy  (Firebase Task<T> -> Flow<DataState<T>>)
├── fetchRestData()     -> RetrofitDataSourceStrategy  (Response<BaseDto<T>> -> Flow<DataState<T>>)
├── fetchPagingData()   -> PagingDataSourceStrategy    (Pager -> Flow<PagingData<T>>)
└── requestData()       -> Direct sync wrapper

Error Flow

Infrastructure Exception
    ↓ ExceptionMapper (data:core)
AppError sealed class (domain:core)
    ↓ DataState.Error or caught in FlowCollectionManager
ErrorHandler.getErrorMessageResId() (feature:core)
    ↓
String resource ID -> Toast

Build Flavors

Three product flavors for environment configuration:

FlavorPurposeProperties file
devDevelopmentenv/dev.properties
stagingQA / Stagingenv/staging.properties
productionProduction releaseenv/production.properties

Build variant format: assembleDevDebug, assembleStagingRelease, assembleProductionRelease

What ships with it

Read from the repository

Just SKILL.md. No reference files, no scripts.

Keep looking

Skills are one crate of 326,679. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.