A fork of our clean architecture boilerplate using the Model-View-Intent pattern

Overview

Build Status codecov Codacy Badge

Android Clean Architecture MVI Boilerplate

Note: This is a fork of our original Clean Architecture Boilerplate, except in this repo we have switched out the MVP approach found in the presentation layer to now use ViewModels from the Android Architecture Components Library. The caching layer now also uses Room.

Welcome 👋 We hope this boilerplate is not only helpful to other developers, but also that it helps to educate in the area of architecture. We created this boilerplate for a few reasons:

  1. To experiment with modularisation
  2. To experiment with the Android Architecture Components
  3. To share some approaches to clean architecture, especially as we've been talking a lot about it
  4. To use as a starting point in future projects where clean architecture feels appropriate

It is written 100% in Kotlin with both UI and Unit tests - we will also be keeping this up-to-date as libraries change!

Disclaimer

Note: The use of clean architecture may seem over-complicated for this sample project. However, this allows us to keep the amount of boilerplate code to a minimum and also demonstrate the approach in a simpler form.

Clean Architecture will not be appropriate for every project, so it is down to you to decide whether or not it fits your needs 🙂

Languages, libraries and tools used

Requirements

Architecture

The architecture of the project follows the principles of Clean Architecture. Here's how the sample project implements it:

architecture

The sample app when run will show you a simple list of all the Bufferoos (Buffer team members!).

Drawing

Let's look at each of the architecture layers and the role each one plays :)

architecture

User Interface

This layer makes use of the Android Framework and is used to create all of our UI components to display inside of the Browse Activity. The layer receives its data from the Presentation layer and when retrieved, the received models are mapped using the Bufferoo Mapper so that the model can be mapped to this layer's interpretation of the Bufferoo instance, which is the BufferooViewModel. The Activity makes use of the BrowseBufferoosViewModel to retrieve data.

Presentation

This layer's responsibility is to handle the presentation of the User Interface, but at the same time knows nothing about the user interface itself. This layer has no dependence on the Android Framework, it is a pure Kotlin module. Each ViewModel class that is created implements the ViewModel class found within the Architecture components library. This ViewModel can then be used by the UI layer to communicate with UseCases and retrieve data. The BrowseBufferoosViewModel returns an instance of a BrowseUiModel which contains data that can be used by the UI.

The ViewModels use an instance of a FlowableUseCase from the Domain layer to retrieve data. Note here that there is no direct name reference to the UseCase that we are using - we do inject an instance of the GetBufferoos UseCase, however.

The ViewModel receives data from the Domain layer in the form of a Bufferoo. These instances are mapped to instance of this layers model, which is a BufferooView using the BufferooMapper.

Domain

The domain layer responsibility is to simply contain the UseCase instance used to retrieve data from the Data layer and pass it onto the Presentation layer. In our case, we define a GetBufferoos - this use case handles the subscribing and observing of our request for data from the BufferooRepository interface. This UseCase extends the FlowableUseCase base class - therefore we can reference it from outer layers and avoid a direct reference to a specific implementation.

The layer defines the Bufferoo class but no mapper. This is because the Domain layer is our central layer, it knows nothing of the layers outside of it so has no need to map data to any other type of model.

The Domain layer defines the BufferooRepository interface which provides a set of methods for an external layer to implement as the UseCase classes use the interface when requesting data.

architecture

Data

The Data layer is our access point to external data layers and is used to fetch data from multiple sources (the cache and network in our case). It contains an implementation of the BufferooRepository, which is the BufferooDataRepository. To begin with, this class uses the BufferooDataStoreFactory to decide which data store class will be used when fetching data - this will be either the BufferooRemoteDataStore or the BufferooCacheDataStore - both of these classes implement the BufferooDataStore repository so that our DataStore classes are enforced.

Each of these DataStore classes also references a corresponding BufferooCache and BufferooRemote interface, which is used when requesting data from an external data source module.

This layers data model is the BufferooEntity. Here the BufferooMapper is used to map data to and from a Bufferoo instance from the domain layer and BufferooEntity instance from this layer as required.

Remote

The Remote layer handles all communications with remote sources, in our case it makes a simple API call using a Retrofit interface. The BufferooRemoteImpl class implements the BufferooRemote interface from the Data layer and uses the BufferooService to retrieve data from the API.

The API returns us instances of a BufferooModel and these are mapped to BufferooEntity instance from the Data layer using the BufferooEntityMapper class.

Cache

The Cache layer handles all communication with the local database which is used to cache data.

The data model for this layer is the CachedBufferoo and this is mapped to and from a BufferooEntity instance from the Data layer using the BufferooEntityMapper class.

Conclusion

We will be happy to answer any questions that you may have on this approach, and if you want to lend a hand with the boilerplate then please feel free to submit an issue and/or pull request 🙂

Again to note, use Clean Architecture where appropriate. This is example can appear as over-architectured for what it is - but it is an example only. The same can be said for individual models for each layer, this decision is down to you. In this example, the data used for every model is exactly the same, so some may argue that "hey, maybe we don't need to map between the presentation and user-interface layer". Or maybe you don't want to modularise your data layer into data/remote/cache and want to just have it in a single 'data' module. That decision is down to you and the project that you are working on 🙌🏻

Thanks

A special thanks to the authors involved with these two repositories, they were a great resource during our learning!

Comments
  • Is there two kinds of ViewModel or just one?

    Is there two kinds of ViewModel or just one?

    I was just working on a similar architecture when I ran into this boilerplate. So far I think it's great and it completes a lot of the thoughts and inner dilemmas I was having.

    One particular problem I keep running into comes down to what the responsibility of the ViewModel is. As in here, the architecture component ViewModel (presentation layer *1) is responsible for receiving Ui events and return Ui states. On the other hand, models from other layers get mapped to another kind of ViewModel (UI layer *2) which is only a representation of the data to be displayed.

    1. BrowsBuferoosViewModel.
    2. BuferooViewModel

    In my project I'm using databinding and so I bind the UI layer ViewModel to list item layouts, for example. In the case of the whole screen, I bind the activity/fragment instead of the presentation layer ViewModel. But I know a colleague binds the layout directly to the presentation layer ViewModel and might have a RecyclerView adapter directly in the VM.

    Would you agree with me that the two kinds of ViewModel I mention are different? Or do you consider them the same? If they are different, shouldn't we find a better naming for one of them?

    opened by GSala 3
  • How should we use CompletableUseCases in the Processor ?

    How should we use CompletableUseCases in the Processor ?

    FlowableUseCases returns a Flowable emiting items, which can be mapped by a processor for it to emit a Result.

    I can't find a way to have the Processor react on Completable.complete() returns from a CompletableUseCase and transform that to an emitted Result.

    Thanks!

    opened by remiberthoz 1
  • ViewModel from View instead of Activity or Fragment

    ViewModel from View instead of Activity or Fragment

    When trying to encapsulate some behavior in a View instead of an Activity or a Fragment, I do not have access to the ViewModelProvider. What would be the mot appropriate way of having the same architecture when using Views?

    I can see at least 4 options.

    1. Pass the ViewModelProvider as an argument to the View constructor
    2. Pass the already obtained ViewModel as an argument to the View constructor
    3. Pass a LifecycleOwner as an argument to the View constructor
    4. Hold the ViewModel in the containing Fragment/Activity and expose events from the view and consume states.
    opened by GSala 1
  • build(gradle): Update obsolete build dependencies

    build(gradle): Update obsolete build dependencies

    • Refactor to use jvmTarget from 1.7 to 1.8
    • Remove buildToolsVersion 26.0.2 to use 28.0.3 by default
    • Refactor compile to implementation
    • Refactor provided to compileOnly
    • Modify targetSdk and compileSdk version from 26 to 28
    • Change kotlin-stdlib-jre8 to kotlin-stdlib-jdk8
    • Fix jacoco version to 0.1.4 to support gradle 5.4.* & above
    • Update gradle tools to 3.5.1
    • Update kotlin plugin version to 1.3.50
    • Update gradle wrapper to 5.4.1
    opened by mochadwi 0
  • Update Gradle version to latest version

    Update Gradle version to latest version

    I have updated Gradle wrapper version to : 4.10.1 And Gradle version to : 3.3.0 And Kotlin version to : 1.3.11

    I receive two Kotlin compiler error : 1 - error: no interface expected here public class ViewModelFactory extends android.arch.lifecycle.ViewModelProvider.Factory {

    2 - error: @Binds methods must have only one parameter whose type is assignable to the return type public abstract android.arch.lifecycle.ViewModelProvider.Factory bindViewModelFactory(@org.jetbrains.annotations.NotNull()

    Full question can be found at : https://stackoverflow.com/questions/54270912/dagger2-and-architectural-components-kotlin-compiler-error

    Could you help to resolve errors?

    opened by alirezaeiii 0
  • Migration from RxJava to Flow

    Migration from RxJava to Flow

    I would love it if you'll migrate your codebase from RxJava to the Flow library. You are already using Kotlin, and it'll be amazing if you'll migrate your project to the Flow library. It'll make your code cleaner.

    opened by raza-bukhari 0
  • More Documentation on how the presentation layer works.

    More Documentation on how the presentation layer works.

    I am a new learner and I am having a hard time trying to figure of the data flow happening in the presentation layer. A little more explanation would help a beginner greatly.

    opened by roshanpisharody 0
  • Unable to get provider android.arch.lifecycle.ProcessLifecycleOwnerInitializer

    Unable to get provider android.arch.lifecycle.ProcessLifecycleOwnerInitializer

    Trying run in Emulator: Nexus 5 with Android 4.4 x86

    12-26 10:25:33.999 4036-4036/org.buffer.android.boilerplate.ui E/AndroidRuntime: FATAL EXCEPTION: main Process: org.buffer.android.boilerplate.ui, PID: 4036 java.lang.RuntimeException: Unable to get provider android.arch.lifecycle.ProcessLifecycleOwnerInitializer: java.lang.ClassNotFoundException: Didn't find class "android.arch.lifecycle.ProcessLifecycleOwnerInitializer" on path: DexPathList[[zip file "/data/app/org.buffer.android.boilerplate.ui-1.apk"],nativeLibraryDirectories=[/data/app-lib/org.buffer.android.boilerplate.ui-1, /vendor/lib, /system/lib]] at android.app.ActivityThread.installProvider(ActivityThread.java:4793) at android.app.ActivityThread.installContentProviders(ActivityThread.java:4385) at android.app.ActivityThread.handleBindApplication(ActivityThread.java:4325) at android.app.ActivityThread.access$1500(ActivityThread.java:135) at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1256) at android.os.Handler.dispatchMessage(Handler.java:102) at android.os.Looper.loop(Looper.java:136) at android.app.ActivityThread.main(ActivityThread.java:5017) at java.lang.reflect.Method.invokeNative(Native Method) at java.lang.reflect.Method.invoke(Method.java:515) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:779) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:595) at dalvik.system.NativeStart.main(Native Method) Caused by: java.lang.ClassNotFoundException: Didn't find class "android.arch.lifecycle.ProcessLifecycleOwnerInitializer" on path: DexPathList[[zip file "/data/app/org.buffer.android.boilerplate.ui-1.apk"],nativeLibraryDirectories=[/data/app-lib/org.buffer.android.boilerplate.ui-1, /vendor/lib, /system/lib]] at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:56) at java.lang.ClassLoader.loadClass(ClassLoader.java:497) at java.lang.ClassLoader.loadClass(ClassLoader.java:457) at android.app.ActivityThread.installProvider(ActivityThread.java:4778) at android.app.ActivityThread.installContentProviders(ActivityThread.java:4385)  at android.app.ActivityThread.handleBindApplication(ActivityThread.java:4325)  at android.app.ActivityThread.access$1500(ActivityThread.java:135)  at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1256)  at android.os.Handler.dispatchMessage(Handler.java:102)  at android.os.Looper.loop(Looper.java:136)  at android.app.ActivityThread.main(ActivityThread.java:5017)  at java.lang.reflect.Method.invokeNative(Native Method)  at java.lang.reflect.Method.invoke(Method.java:515)  at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:779)  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:595)  at dalvik.system.NativeStart.main(Native Method) 

    opened by jjmaciasdev 1
Owner
Buffer
Helping people build their business on social media since 2011
Buffer
An android boilerplate project using clean architecture

Android Clean Architecture Boilerplate Welcome ?? We hope this boilerplate is not only helpful to other developers, but also that it helps to educate

Buffer 3.6k Jan 4, 2023
Bego Chat is chat application in Kotlin and Firebase with the following features: last seen , user status like typing ,online and last seen with MVVM pattern and clean architecture

Compose ChatApp(Bego Chat) Bego Chat is Compose chat application in Kotlin and Firebase with the following features: sending all file types and abilit

Ahmed EL Bagory 5 Dec 20, 2022
Skeleton project for show the architecture of Android project using MVVM, Clean Architecture and Kotlin coroutine Flow

ClearScoreDemo Skeleton project for showing the architecture of Android project using MVVM, Clean architecture and Kotlin coroutine Flow App Architect

Plabon Modak 1 Mar 6, 2022
FaceTimeClone app that implements Coroutines , mvvm architecture , clean architecture , navigation component , hilt , etc.... using kotlin language

This repository contains a FaceTimeClone app that implements Coroutines , mvvm architecture , clean architecture , navigation component , hilt , etc.... using kotlin language

null 17 Dec 13, 2022
Boilerplate code for implementing MVVM in Android using Jetpack libraries, coroutines, dependency injection and local persistance

MVVM Foundation This projects aims to speed up development of Android apps by providing a solid base to extend Libraries Jetpack Fragment Material3 :

Gabriel Gonzalez 2 Nov 10, 2022
MVIExample - A sample app showing how to build an app using the MVI architecture pattern

MVIExample A sample app showing how to build an app using the MVI architecture p

Yasser AKBBACH 0 Jan 8, 2022
Reapp is everything you need to build amazing apps with React: a collection of packages that work together, our UI kit, and a CLI that scaffolds your app and includes a server and build system.

What is it? Reapp is everything you need to build amazing apps with React: a collection of packages that work together, our UI kit, and a CLI that sca

reapp 3.4k Nov 20, 2022
A Kotlin library for reactive and boilerplate-free SharedPreferences in Android

KPreferences A Kotlin library for reactive and boilerplate-free Shared Preferences in Android. With KPreferences you can use Kotlin's marvelous delega

Mohamad Amin Mohamadi 19 Dec 16, 2020
An Android template project (in Kotlin) with boilerplate and current patterns.

android-starter-v4 An Android template project (in Kotlin) with boilerplate and plumbing, exploring current architecture patterns. A bit too much for

Matthias Urhahn 14 Nov 4, 2022
👋 A common toolkit (utils) ⚒️ built to help you further reduce Kotlin boilerplate code and improve development efficiency. Do you think 'kotlin-stdlib' or 'android-ktx' is not sweet enough? You need this! 🍭

Toolkit [ ?? Work in progress ⛏ ?? ??️ ?? ] Snapshot version: repositories { maven("https://s01.oss.sonatype.org/content/repositories/snapshots") }

凛 35 Jul 23, 2022
Android-Boilerplate - Base project for android development with new technology

Android-Boilerplate Base project for android development with new technology, in

Muhammad Rizky Arifin 1 Aug 15, 2022
Crunch-Mobile - A Food Delivery Mobile App which uses Modern App Architecture Pattern, Firebase And a Simple Restful Api

Crunch-Mobile This is a Food Delivery Mobile App which uses Modern App Architect

Bright Ugwu 1 Jan 1, 2022
A FDPClient fork , It aims to add more modules.

LightClient A FDPClient fork , It aims to add more modules. You can download development version at Github-Actions , Release at Release Only running o

Ad973_ 3 Aug 26, 2021
Kotlin Multiplatform lifecycle-aware business logic components (aka BLoCs) with routing functionality and pluggable UI (Jetpack Compose, SwiftUI, JS React, etc.), inspired by Badoos RIBs fork of the Uber RIBs framework

Decompose Please see the project website for documentation and APIs. Decompose is a Kotlin Multiplatform library for breaking down your code into life

Arkadii Ivanov 819 Dec 29, 2022
A high-performance fork of Paper/Airplane designed for large servers.

Pufferfish A highly optimized Paper/Airplane fork designed for large servers requiring both maximum performance, stability, and "enterprise" features.

Pufferfish Studios LLC 399 Jan 7, 2023
Minecraft Server Software specially designed for Thicc SMP. Here on GitHub without the private patches, just a normal hybrid JettPack-Pufferfish-Empirecraft fork

AlynaaMC A private, custom server software for Thicc SMP and a fork of Pufferfish. Here on GitHub with patches from JettPack, Airplane and Pufferfish

ThiccMC 14 Dec 31, 2021
A fork from Paper and unofficial Airplane continuation for RedeObscurity.

Obscurity A fork from Paper and unofficial Airplane continuation for RedeObscurity. Features Downloads This fork is in a state of development. If you

null 0 Jan 6, 2022
WolfxPaper - A Paper fork designed for Wolfx Survial, may useful for some Semi-Vanilla Server

WolfxPaper A Paper fork designed for Wolfx Survial, may useful for some "Semi-Va

TenkyuChimata 1 Jan 19, 2022