Skip to content

Modern Android development example built with Kotlin, Jetpack Compose, Clean Architecture and MVVM.

License

Notifications You must be signed in to change notification settings

hbjosemaria/Watcha

Repository files navigation

Watcha

Watcha is a sample of modern Android development built with Kotlin, Jetpack Compose, MVVM pattern and Clean Architecture as its base, and additionally, on top of that: Flows, Coroutines, DI (Hilt), Room and Retrofit.

Note

Soon there will be available a release build so you can check this project downloading the app from Google Play Store.

📋 Previous configuration

✅ Create a TMDB API key
✅ Create a Google ID API key
✅ Store both API keys within local.properties file
✅ Configure a Firebase Auth access

⚙️ Project structure: tech stack and dependencies

  • Minimum SDK: 31.

  • Kotlin based, taking advantage of its functional programming feature as well as Coroutines and Flow for asynchronous operations.

  • UI/UX:

    • Jetpack Compose: latest UI toolkit for building seamless modularized and reusable components.
    • Compose navigation: facilitates navigation between screens.
      • Nested navigation: Compose Navigation brings the opportunity to add nested graphs, making it easier to route user's destinations by features.
    • Hilt compose: allowing direct ViewModel DI into composables.
    • Paging compose: collecting data with LazyPagingItems and consume them in LazyLists.
  • Architecture and UI pattern:

    • Clean Architecture: establish the project structure based in three main layers: UI layer, Domain layer and Data layer. Each one of these can contain more related sub-layers.
    • MVVM pattern (Model - View - ViewModel) and elevated state holders: UI is split in three different concerns - UI logic, UI data management, and independent state holder classes.
    • ViewModel: main class for managing screen data in UI layer. It's responsible of caching data to persist after configuration changes and expose events to be consumed by composables, and it's also aware of its lifecycle, facilitating its memory unbinding after the composable which contains it is disposed.
  • Dependencies:

    • Hilt: adds dependency injection, easing up project structure and reducing code boilerplate.
    • Room: creates and manages SQLite databases with simple and fast instructions.
    • Retrofit: allows making HTTP calls to external resources, like RESTful APIs, to ask for data.
    • GSON: used to parse data within HTTP calls between JSON and Dto Classes.
    • Paging: powerful library which manages loading large datasets from a local or remote source in an efficient way.
      • PagingSource: manages both PagingData data loading per page and its status from remote or local sources.
      • RemoteMediator: similar to PagingSource, but additionally stores data in a local database, like Room, to make it the single source of truth.
    • Coil: lightweight library that brings a powerful way to load Images based in Coroutines.
    • Credential Manager (email + password, Google Sign In): as the AccountManager successor, this Jetpack API adds a modern way to authenticate users by id+password, federated sign-in solutions (Google Sign In) and passkeys.
    • Firebase (Authentication): wide library with several modules that enhances the app quality. Authentication is one of these and adds a secure way to authenticate and store user credentials.

💊 Testing: types and dependencies

  • Tests: Watcha includes some unit, integration, UI and E2E tests as example, but have in mind a few are outdated with the latest changes.
  • Dependencies and API:
    • Compose Testing: adds rules that allows composables testing and interaction.
    • Hilt Testing: adds rules to bring DI. Besides that, brings the opportunity to replace existing Modules with fake ones to make testing easier.
    • JUnit: base unit testing class.
    • MockK: Mocking library specialized in Kotlin to create stubs and mocks.
    • Espresso (Intents): core API for UI, integration and E2E tests. As Watcha is built with Compose, only its Intent extension is being used.
    • Robolectric: it brings some limited Android SDK functionalities that can be beneficial for unit testing.
    • Truth: lightweight library which makes asserts easier and more legible.
    • Turbine: powerful library for testing Kotlin Coroutines and Flows.

🔧 Extra features

  • Caching system for offline access by using Pager RemoteMediator combined with Room, therefore most commonly accessed data will be available even if the user's device has no connection.
  • Custom LoadStates per ViewModel for better UX.
  • Daily scheduled data fetching with workers for storing it in the local SQLite database.
  • App language selection, with its consequent API call language adaption.
  • User synchronization between its Watcha identity and TMDB identity with its TMDB session_id.
    • By now this link is established only for fetching its profile data, but it can be expanded as much as liked to other features like: lists management, movie/tv shows favorites, ratings, reviews, etc.

🧩 Project architecture

General overview

Based on Clean Architecture, there are three main layers separated by concerns: UI Layer, Domain Layer and Data Layer. By doing so, it establishes a clean, robust, maintainable, reusable and scalable structure which makes it easier to be tested. Also, it defines a unidirectional event/data flow between these three layers, consuming events the deeper it goes and retrieving data to the outer layer.

UI Layer

The outer layer, the UI Layer, has three main priorities:

  • Display data to user: composables are responsible to display collected data to user.
  • React to data changes: composables are always observing state holder data changes, so they recompose itself with new data.
  • Manage screen data: each screen has its own ViewModel associated, which is responsible to manage the state holder data, expose events to be consumed by users and trigger operations in the Domain Layer.

Domain Layer

The Domain Layer, the middle layer, contains all the project business logic, meaning its main purpose is executing data operations related to the business needs: mapping, sorting, filtering, etc. The most important characteristic is that this layer is platform free and must have only Java/Kotlin code to maintain its purity.

Here, Use cases, also known as Interactors, are responsible to exposing its events to UI Layer to be consumed and asking the Data Layer for data operations.

Data Layer

The Data Layer, which is the lowest one, has only one focus: managing data from different sources, which includes performing CRUD operations, manage user data and so on. Under the hood, there can be several sources depending on the data definition. Watcha also applies the repository pattern, which compensates an extra layer with more flexibility and control over those services contracts.

Here repositories keep the event/data flow persistent as they expose events to be consumed by Domain Layer and performs data operations within its several associated services.

💻 Public API

Watcha uses TMDB public API as its main external data source, which is a RESTful API.

💜 Support this project

If you find this repository illustrative and helpful, I kindly ask you to:

  • Give this repository a star ⭐
  • Follow me for more modern Android Development content 🤗

🎞️ Extra: preview gallery

Here you can take a quick overview about what Watcha does and what you can achieve by using Kotlin and Jetpack Compose as its base.

Home

Movie details

Favorite list

YouTube trailer

Search

Language selection

Caching system

Error management

Sign In and authorization

User session management