Architecture Overview
Relevant source files
The following files were used as context for generating this wiki page:- app/src/main/java/com/vocabri/ui/main/viewmodel/MainViewModel.kt
- domain/src/main/java/com/vocabri/domain/usecase/word/ObserveWordGroupsUseCase.kt
- data/src/main/java/com/vocabri/data/repository/word/WordRepositoryImpl.kt
- README.md
- app/src/main/java/com/vocabri/di/AppModule.kt
- data/src/main/java/com/vocabri/data/di/DataModule.kt
- app/src/main/java/com/vocabri/ui/screens/dictionary/viewmodel/DictionaryViewModel.kt
Vocabri is a modern, offline-first Android application designed for personal vocabulary building. Its architecture is structured around a multi-module setup that promotes separation of concerns, scalability, and maintainability. The project adheres to principles of Clean Architecture, utilizing an MVVM (Model-View-ViewModel) pattern on the UI layer. This design facilitates a reactive data flow from the persistence layer up to the UI and prepares the codebase for future expansion, including a planned iOS application via a Kotlin Multiplatform shared module.
The core of the architecture is divided into distinct layers: app for UI and presentation logic, domain for business rules and use cases, and data for data persistence and repository implementations. Dependency Injection is managed by Koin, which wires together the components across these layers. Persistence is handled locally by a SQLDelight database, ensuring the app functions seamlessly offline.
Sources: README.md
Project Structure
The project is organized into several Gradle modules, each with a specific responsibility. This modularization enforces a clear separation of concerns and improves build times and code navigation.
Sources: README.md:33-43
This diagram illustrates the dependency flow between the main modules. The app module depends on the domain module, and the data module implements the interfaces defined in the domain module.
Sources: README.md:33-43
Module Responsibilities
| Module | Description |
|---|---|
app | The main Android application module. It contains the Jetpack Compose UI, navigation graphs, ViewModels, and the Koin appModule for UI-layer dependency injection. |
domain | Contains the core business logic, including domain models, repository interfaces (contracts), and use cases (e.g., AddWordUseCase, ObserveWordGroupsUseCase). It is independent of any specific framework or platform. |
data | Implements the repository interfaces from the domain layer. It manages data sources, including the SQLDelight database, and contains the Koin dataModule. |
core | A collection of utility modules. core/logger provides logging abstractions (Kermit), and core/utils contains common utilities like the ID generator abstraction. |
shared | A Kotlin Multiplatform module intended for shared code between Android and future iOS targets. |
build-logic | Contains custom Gradle convention plugins for configuring the build, including settings for Spotless, Detekt, and Kover. |
Sources: README.md:33-43, build-logic/convention/src/main/kotlin/ProjectConventionPlugin.kt
Architectural Pattern and Data Flow
The application employs a layered architecture based on Clean Architecture principles, with MVVM on the presentation layer. This creates a unidirectional data flow and a clear separation between UI, business logic, and data handling.
Key Components
- ViewModel (
appmodule): Manages UI-related data and state. It executes use cases in response to UI events and exposes state as aStateFlowfor the UI to observe. Example:DictionaryViewModel. - UseCase (
domainmodule): Encapsulates a single, specific business rule. It orchestrates data flow from one or more repositories. Example:ObserveWordGroupsUseCase. - Repository Interface (
domainmodule): Defines a contract for data operations, abstracting the data source from the business logic. Example:WordRepository. - Repository Implementation (
datamodule): Implements the repository interface, coordinating data from local and/or remote data sources. Example:WordRepositoryImpl. - DataSource (
datamodule): Directly interacts with a data source, such as the SQLDelight database (WordLocalDataSourceImpl) or a remote API.
This diagram shows the class relationships between the different layers.
Data Flow: Observing Word Groups
The following sequence diagram illustrates the flow of data when the DictionaryScreen is loaded. The process is reactive, using Kotlin Flows to propagate data changes from the database to the UI.
Dependency Injection
The project uses Koin for dependency injection, which simplifies the process of providing and consuming dependencies across different modules. Configuration is defined in separate modules for each layer.
Sources: README.md:25, app/src/main/java/com/vocabri/di/AppModule.kt, data/src/main/java/com/vocabri/data/di/DataModule.kt
Koin Modules
appModule: Located in theappmodule, it is responsible for providing ViewModels (e.g.,MainViewModel,DictionaryViewModel) and Android-specific dependencies likeResourcesRepository. It dynamically createsDictionaryDetailsViewModelinstances for eachPartOfSpeech.
Sources: app/src/main/java/com/vocabri/di/AppModule.kt:31-50dataModule: Located in thedatamodule, it provides data-layer components. This includes theVocabriDatabaseinstance,WordRepository, data sources (WordDataSource), and utility classes likeIdGenerator.
Sources: data/src/main/java/com/vocabri/data/di/DataModule.kt:34-53domainModule: (Not shown in files, but implied by structure) A module in thedomainlayer would provide the use cases, such asObserveWordGroupsUseCase.
This diagram shows how Koin modules are defined in different project layers to provide dependencies.
Persistence
The application uses an offline-first approach for data persistence, with SQLDelight as the database engine. SQLDelight generates type-safe Kotlin APIs from SQL queries, ensuring correctness at compile time and providing reactive Flow-based queries.
- Database Schema: The schema and queries are located in
data/src/main/sqldelight. - Database Name:
vocabri.db. - Driver:
AndroidSqliteDriveris used to connect SQLDelight to the Android platform's SQLite implementation. - Instantiation: The
VocabriDatabaseinstance is created and provided as a singleton via Koin in thedataModule.
Sources: README.md:27, 80-82, data/src/main/java/com/vocabri/data/di/DataModule.kt:35-42
Quality and Tooling
The project incorporates a suite of tools to maintain code quality, managed via custom Gradle convention plugins in the build-logic module.
| Tool | Purpose | Configuration |
|---|---|---|
| Spotless | Code formatting for Kotlin, XML, and other file types. Uses ktlint. | build-logic/convention/src/main/kotlin/com/vocabri/SpotlessConfiguration.kt |
| Detekt | Static code analysis for Kotlin to detect code smells and potential bugs. | build-logic/convention/src/main/kotlin/com/vocabri/DetektConfiguration.kt |
| Kover | Code coverage reporting for unit and instrumented tests. | build-logic/convention/src/main/kotlin/com/vocabri/KoverConfiguration.kt |
Sources: README.md:29-30, 72-78, build-logic/convention/src/main/kotlin/ProjectConventionPlugin.kt:26-29