-
Tech-stack
- 100% Kotlin + Coroutines - perform background operations
- Retrofit - networking
- Jetpack
- Navigation - in-app navigation
- Kotlin Flows - notify views about database change
- Lifecycle - perform an action when lifecycle state changes
- ViewModel - store and manage UI-related data in a lifecycle conscious way
- Room - store offline cache
- Hilt - dependency injection
- Coil - image loading library
-
Modern Architecture
- Clean Architecture (at feature module level)
- Single activity architecture using NavComponent
- MVVM (presentation layer)
- Android Architecture components (ViewModel, LiveData, Navigation)
- Android KTX - Jetpack Kotlin extensions
-
Testing
- Unit Tests (JUnit 5 via android-junit5)
- Integration and End-to-end tests
- Mockk - mocking framework
-
UI
-
Gradle
- Gradle Kotlin DSL
- Gradle KTS Kotlin script
- Custom tasks
- Plugins (SafeArgs, android-junit5)
Feature related code is placed inside one of the feature modules. We can think about each feature as the reusable component, equivalent of microservice or private library.
The modularized code-base approach provides few benefits:
- better separation of concerns. Each module has a clear API., Feature related classes live in different modules and can't be referenced without explicit module dependency.
- features can be developed in parallel eg. by different teams
- each feature can be developed in isolation, independently from other features
- faster compile time
We have different kinds of modules in the application:
app
module - this is the main module. It contains code that wires multiple modules together and fundamental application configuration.buildSrc
module - this module contains all version values so they can be reused/changed easily throught all the .build filescore UI
module - contains all shared data related to UI (this allows us to not import compose dependencies in data and domain modules)core
module - contains all shared data apart from UI relatedfeature
modules - the most common type of module containing all code related to a given feature (containing data , domain and presentation submodules)
Clean architecture
is the "core architecture" of the application, so each feature module
contains own set of Clean architecture layers:
Each feature module contains non-layer components and 3 layers with distinct set of responsibilities.
This layer is closest to what the user sees on the screen. The presentation
layer is designed with MVVM
- Jetpack ViewModel
used to preserve data across activity restart, actions
modify the common state
of the view and then new state is edited to a view via Kotlin Flows
to be rendered).
Components:
- View (Fragment) - presents data on the screen and pass user interactions to View Model. Views are hard to test, so they should be as simple as possible.
- ViewModel - dispatches (through
Kotlin Flows
) state changes to the view and deals with user interactions. - ViewState - common state for a single view
This is the core layer of the application. Notice that the domain
layer is independent of any other layers. This allows to make domain models and business logic independent from other layers.
In other words, changes in other layers will have no effect on domain
layer eg. changing database (data
layer) or screen UI (presentation
layer) ideally will not result in any code change withing domain
layer.
Components:
- UseCase - contains business logic
- DomainModel - defines the core structure of the data that will be used within the application. This is the source of truth for application data.
- Repository interface - required to keep the
domain
layer independent from thedata layer
(Dependency inversion).
Manages application data and exposes these data sources as repositories to the domain
layer. Typical responsibilities of this layer would be to retrieve data from the internet and optionally cache this data locally.
Components:
-
Repository is exposing data to the
domain
layer. Depending on application structure and quality of the external APIs repository can also merge, filter, and transform the data. The intention of these operations is to create high-quality data source for thedomain
layer, not to perform any business logic (domain
layeruse case
responsibility). -
Mapper - maps
data model
todomain model
(to keepdomain
layer independent from thedata
layer). -
RetrofitService - defines a set of API endpoints.
-
DataModel - defines the structure of the data retrieved from the network and contains annotations, so Retrofit (Moshi) understands how to parse this network data (XML, JSON, Binary...) this data into objects.
This project utilizes multiple mechanics to easily share the same versions of dependencies such as base-module to be interited from and BuildSrc module containing all version values to be reused.
Read related articles to have a better understanding of underlying design decisions and various trade-offs.
Android Studio
->File
->New
->From Version control
->Git
- Enter
https://github.com/YosifKalchev/CalorieTracker.git
into URL field an pressClone
button
- Run
git clone https://github.com/YosifKalchev/CalorieTracker.git
command to clone project - Open
Android Studio
and selectFile | Open...
from the menu. Select cloned directory and pressOpen
button
- Core App Quality Checklist - learn about building the high-quality app
- Kotlin Coroutines - Use Cases on Android - most popular coroutine usages
- to be updated...