Smart Feed is a showcase Android application demonstrating modern, production-grade architectural patterns. It features a modular, offline-first article feed, dynamic filtering/sorting, and an on-device recommendation engine powered by text embeddings.
A reference project demonstrating scalable Android architecture. The core focus is on building predictable systems using Feature-Driven Vertical Slices with strict modular boundaries (API / Local / Impl). It utilizes Decompose for lifecycle-aware navigation, while architectural constraints and code quality are automatically enforced via Konsist, Detekt 2, and Spotless.
The project is structured under Clean Architecture guidelines with Feature-Driven Vertical Slice decomposition and Component-Driven UI navigation:
- Vertical Feature Slices: Each feature owns its full stack — domain contracts (
:api), Room entities and DAOs (:local), and all UI and infrastructure implementations (:impl). This eliminates the "horizontal monolith" anti-pattern.app └─ feed:impl ├─ feed:api ├─ feed:local ├─ recommendation:api └─ core contractsapiis the stable feature contract.localowns the Room schema without circular dependencies.implcontains UI, Store, repositories, and Android-specific integrations. - 3-Module Feature Structure: The
:localmodule is a deliberate architectural solution to prevent circular Gradle dependencies caused by Room's@Databaseentity registration requirement. See Architecture Documentation. - Decompose Navigation: Pure Kotlin component tree controlling lifecycle, state preservation, and back-stack handling — navigation and state ownership are decoupled from Android UI implementations. See ADR 0001.
- Executable Architecture Guards (Konsist): A dedicated
:architecture-testsJVM module enforces module boundary rules on every CI build — preventing domain leakage, platform imports in API modules, and naming violations. - Consolidated Gradle Build-Logic: Modern composite
build-logiceliminating oldbuildSrc. Convention plugins handle per-module Detekt profiles, Spotless formatting, and toolchain configuration. - Compose-Only UI Runtime: The production UI path is now fully Jetpack Compose while retaining Decompose as the navigation/state backbone. The earlier XML-to-Compose island phase remains documented as the migration path and benchmark baseline. See ADR 0002 and Performance Results.
For a complete breakdown, see the Architecture Documentation.
| Layer | Technologies |
|---|---|
| Language | Kotlin 2.3.21, JVM 17 target |
| Build | Android Gradle Plugin 9.2.1, Gradle 9.4.1, KSP 2.3.9, composite build-logic |
| Navigation | Decompose 3.3.0 with Compose rendering (extensions-compose) |
| State | MVIKotlin 4.2.0 for complex Feed components; simple coordinators remain Decompose components |
| Database | Room 2.7.1 with float-array embedding converters, per-feature entity/DAO modules (:local), Paging 3 (PagingData, GetPagedContentUseCase) owned by :feature:feed:impl |
| Background | WorkManager with Hilt worker scheduling |
| DI | Dagger Hilt 2.60 (assisted factories, interface binds, per-feature Hilt modules) |
| Images | Coil 3 for Compose UI plus a pure Kotlin ImageLoader contract (:core:image:api) for shared/background image work |
| Static Lint | Detekt 2.0.0-alpha.5 (layered profiles), Spotless 6.25.0 / Ktlint |
| Arch Testing | Konsist 0.17.3 — executable architecture guards |
| Networking | Retrofit + OkHttp, Ktor local mock server for dev flavour |
Smart Feed includes a fully local recommendation pipeline based on article text embeddings. No reading behavior leaves the device.
User reads article
↓
engagementWeight = 0.5×readPercentage + 0.5×normalizedTime
↓
Weighted moving average updates user interest vector
(old_vec×visitCount + article_vec×weight) / (visitCount+1)
↓
Article embeddings (FloatArray, unit-norm) loaded from Room
Already-read articles fully excluded
↓
top-K candidates (highest cosine similarity to user vector)
cold-K candidates (opposite vector → diversity / serendipity)
↓
MMR diversification: λ×sim(candidate,profile) − (1−λ)×max_sim_to_selected
↓
Ranked recommendations persisted to Room → displayed in feed
The algorithm lives entirely in :feature:recommendation:impl — isolated, independently
testable, and offline-first. Room schema is owned by :feature:recommendation:local.
Public contracts (use cases, repository interfaces) live in :feature:recommendation:api.
For full details, see Recommendation Engine.
smart-feed/
├── app/ # Composition root (Hilt, AppBootstrapper, MainActivity)
├── architecture-tests/ # Konsist architecture enforcement tests (pure JVM, no Android)
├── build-logic/ # Convention plugins: toolchain, Detekt, Spotless, feature config
├── config/detekt/ # Layered Detekt rule profiles (domain, data, ui, test, common)
├── core/
│ ├── common/ # Pure Kotlin shared utilities: coroutine helpers, embedding math,
│ │ # time converters (formerly :core:core → renamed)
│ ├── analytics/
│ │ ├── api/ # AnalyticsService interface (pure Kotlin)
│ │ └── impl/ # Analytics implementation
│ ├── connectivity/ # ConnectivityRepository (network state, modern observer-based)
│ ├── content/api/ # Shared content value objects used across features
│ ├── core-database/ # RoomDatabase orchestrator, cross-feature schema migrations
│ ├── core-networks/ # Retrofit/Ktor config, prod & dev network data sources
│ ├── coroutines/ # Coroutine Dispatchers DI module
│ ├── image/api/ # Pure Kotlin ImageLoader contract (KMP-portable)
│ └── lifecycle/ # AppLifecycleObserver
├── docs/
│ ├── adr/ # Architectural Decision Records
│ └── plans/ # Implementation and refactoring plans
└── feature/
├── feed/ # Article feed — full vertical slice
│ ├── api/ # Component contracts, ContentItem domain model, repository API
│ ├── local/ # ContentEntity, ContentDao (feed-owned Room storage)
│ └── impl/ # Compose UI, Hilt modules, repository impls,
│ # Paging 3 (ContentPagingRepository, GetPagedContentUseCase)
├── recommendation/ # Recommendation engine — full vertical slice
│ ├── api/ # Recommendation contracts, models (Recommendation, Recommender,
│ │ # RecommendationRepository, RecommendForUserUseCase, RecommendForArticleUseCase)
│ ├── local/ # Room schema: ArticleEmbedding, ArticleEmbeddingDao,
│ │ # ContentInteractionStats, ContentInteractionStatsDao,
│ │ # UserRecommendationEntity, ContentRecommendationEntity, RecommendationDao
│ └── impl/ # RecommenderImpl (cosine similarity + MMR + cold-picks),
│ # EmbeddingIndex, RecommendationRepositoryImpl, Hilt modules
└── userprofile/ # User identity — vertical slice
├── api/ # UserProfile model, UserProfileRepository contract
├── impl/ # Repository impl, Hilt module
└── local/ # User profile Room storage
| Phase | Description | Status |
|---|---|---|
| 0 | Stash WIP (create-post-wip), isolate polish steps |
✅ Done |
| 1 | Architecture docs, ADRs, diagrams | ✅ Done |
| 2 | Build logic consolidation (buildSrc → build-logic), AGP 9.2.1, Kotlin 2.3.21, KSP 2.3.9, Detekt 2 |
✅ Done |
| 3 | Konsist architecture enforcement module (:architecture-tests) |
✅ Done |
| 4 | MainActivity decoupling — AppStartupCoordinator, SystemBarsController, Compose app shell |
✅ Done |
| 5 | AndroidX Paging dependency inversion — extracted to :core:core-paging, then co-located into :feature:feed:impl (sole consumer) |
✅ Done |
| 6 | Feature API/Impl split — :feature:feed:api and :feature:feed:impl |
✅ Done |
| 7 | Core Layer Modularization — 3-module feature slices (api/local/impl), :core:core → :core:common, eliminated core-domain / core-data / core-paging monoliths, build noise cleanup |
✅ Done |
| 8 | MVIKotlin stores for Feed List, Recommendations, Article, and Article Recommendations with reducer/component tests | ✅ Done |
| 9 | Parallel Compose ArticleCard track with XML parity, performance comparison, and Baseline Profiles |
✅ Done |
| 10 | Full Jetpack Compose migration of remaining screens and navigation | ✅ Done |
| 11 | Image Prefetching and migration to Coil for Compose | 📝 Planned |
- Clone the repository:
git clone https://github.com/nikkiw/smart-feed.git cd smart-feed - Run the full quality gate locally:
# Auto-format code ./gradlew spotlessApply # Static analysis (layered Detekt profiles) ./gradlew detekt # Architecture consistency tests (Konsist) ./gradlew :architecture-tests:test # All unit tests ./gradlew test # Assemble developer debug build ./gradlew assembleDevDebug
- Full CI verification matrix in one command:
./gradlew spotlessCheck detekt :architecture-tests:test test
Licensed under the Apache License 2.0. See LICENSE for details.
Made by Nikolay Vlasov – Android Architect.
