Code Monkey home page Code Monkey logo

android-clean-architecture-boilerplate's Introduction

Build Status codecov Codacy Badge

Android Clean Architecture Boilerplate

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 share some approaches to clean architecture, especially as we've been talking a lot about it
  3. 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 BrowseContract to enable communication to and from the presenter.

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 Presenter class that is created implements the Presenter interface defined within an instance of a contract - in this case the BrowseContract, which also contains an interface for the View interface.

When a Presenter is constructed, an instance of this View is passed in. This view is then used and the presenter is set for it using the implemented setPresenter() call.

The presenters use an instance of a SingleUseCase 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 presenter 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 SingleUseCase 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!

android-clean-architecture-boilerplate's People

Contributors

albodelu avatar grepx avatar hitherejoe avatar jawnnypoo avatar ldhnam avatar lekaha avatar meisolsson avatar ravidsrk avatar shikherverma avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

android-clean-architecture-boilerplate's Issues

Where do WorkManager, JobScheduler Fit?

Usually, in apps, there are some tasks that we run in the background, such as syncing of data, analytics etc. Google suggested to use deferrable components such as WorkManager and JobScheduler in such instances.
Where do these components fit in Clean Architecture?

DataStore question

Hi, i have a question. Why did you introduce DataStore classes, instead of using DataSource itself?

What is the gain of adding one more abstraction, DataSource is already interface and Repository would not be depended on Implementation any way?

Kotlin coroutines

If you were to implement the same framework in Kotlin coroutines instead of using RxJava2 to avoid callbacks (onSuccess, onError) from presenter, how will you do it? Just curious.. :)

Cheers,

Ren

Wrong comment on `SingleUseCase.kt`#45

First of all, congratulations for this fantastic project.

This is an easy one: The comment I mentioned is wrong. It seems that it's been copied from method fun dispose()

Regards,

Javi.

Unused `unsubscribe` method on `CompletableUseCase`

It looks like we're just creating an empty disposable in this class and disposing of it in unsubscribe without ever changing its value. In the SingleUseCase class, we pass an observer into the execute method and capture a reference to the subscription by subscribing inside of thhat method. I would have expected the same to happen in this class, or if the idea is that we subscribe manually from our presenter, shouldn't we also unsubscribe manually and remove this unsubscribe method altogether?

abstract class CompletableUseCase<in Params> protected constructor(
        private val threadExecutor: ThreadExecutor,
        private val postExecutionThread: PostExecutionThread) {
    // Here we initialize subscription as an empty disposable
    private val subscription = Disposables.empty()
    protected abstract fun buildUseCaseObservable(params: Params): Completable

    // I would have expected an observer to be passed into execute so that we can
    // subscribe in this method and obtain a reference to the subscription 
    fun execute(params: Params): Completable {
        return this.buildUseCaseObservable(params)
                .subscribeOn(Schedulers.from(threadExecutor))
                .observeOn(postExecutionThread.scheduler)
    }

    // Now we unsubscribe from our empty disposable
    fun unsubscribe() {
        if (!subscription.isDisposed) {
            subscription.dispose()
        }
    }
}

what is the purbose of BufferooCache and BufferooRemote interfaces?

I hit this confusion at first when I'm reading BufferooDataStoreFactory and this is because the class depends on both BufferooCache and BufferooCacheDataStore, where I think it could only depends on BufferooCacheDataStore. we can access the BufferooCache instance from it, to be able to use isCached() or isExpired()
and my last question wouldn't be better to just define remote and cache requirements on DataStore interfaces and removing BufferooCache and BufferooRemote ? .. I think this will make things simpler.

what is the purpose of BufferooCache and BufferooRemote?

Questions about the architecture

The use cases assume the caller is also the receiver of the data. In most cases, the presenter will call the use case and wait for the result to return for display purposes.

This is mostly true but there are circumstances when it is not what is wanted. For example, my app has chat functionality which uses a websocket. Incoming messages from the server are received in a Socket class in my data module. I could call a use case to deal with it but (a) it seems dirty that such a low level class is using a use and (b) the result of the use case should notify the presenter (if running) about the event.

Any ideas how to deal with this?

Another issue is that by default, the presenter is running on the UI thread so returning data is also on the UI thread (enforced by the use case infrastructure). However, further processing such as domain model to view model can be required and it would be done on the UI thread by default.

Maybe the thread management should remain with the presenter and the use case is oblivious?

Domain layer and Application layer separation

Hi, one question related to domain project/module...
Can you explain a little bit more what is a reason that use-cases and domain logic are in
the same module, shouldn't it be more appropriate to separate use-cases to application layer
and domain entities with all their logic to domain layer... This way the domain layer is
shareable as enterprise-wide rules....

More on stackexchange: Application layer vs Domain layer

Useless @Inject in `BrowseBufferoosPresenter`

Hello!

Once again thank you for this project.

I've realised you are using @Inject constructor' to the BrowseBufferoosPresenterIs this gonna be neccesary somewhere? I mean, you're well providing the presenter dependency from theBrowseActivityModule. You'll never be able to inject the presenter withouth its module because the BrowseBufferoosContract.View` has to be provided through a module.

Regards!

Separate Module for Injection

Looking at the diagram it seems like Mobile-UI depends only on presentation module. When I actually dived into the code, that is not really the case. Mobile-UI depends on 4 out of 5 modules in the project. As far I could understand the necessity of other modules except the presentation one is because of the injection package we have inside Mobile-UI module.

What I was curious about is that if we could make different module for injection which will depend on all modules and providing the necessary injection required for each module. Doing that we can limit the usage of modules other than presentation from Mobile-UI modules. Which will also separate the concerns in a good day.

Presentation Gradle has wrong rootProject.ext

Presentation gradle has :

def presentationDependencies = rootProject.ext.cacheDependencies
def presentationTestDependencies = rootProject.ext.cacheTestDependencies

but I guess that this has to be something like :

def presentationDependencies = rootProject.ext.presentationDependencies
def presentationTestDependencies = rootProject.ext.presentationTestDependencies

Also in dependencies.gradle, presentationDependencies has to be changed to provide only the necessary libraries.

Domain Layer Mapping

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.

Is there a detailed reason why the Domain Has no Mappings?
wouldn't a Bufferoo fetched from an external source need to be mapped to a Bufferoo in the domain?

How to add a child fragment?

I want to add a child fragment to my fragment, but I can not figure out how to do it. Is there some body can help me ? Pleazzzzzzz

AndroidTest mobile-ui, java.lang.NoClassDefFoundError - Android KitKat

Hi,

Thank you for such interesting project sample.

There seems to be a problem with androidTest from mobile-ui module. It doesn't seem to work on KitKat version of OS.

java.lang.NoClassDefFoundError: org.buffer.android.boilerplate.ui.test.TestRunner$onCreate$1
at org.buffer.android.boilerplate.ui.test.TestRunner.onCreate(TestRunner.kt:14)

The missing class occurs to be RxJavaPlugin class.

What purpose in setPresenter() method inside View wich has injection of that's presenter

override fun setPresenter(presenter: BrowseBufferoosContract.Presenter) {

Hello, I figured that you set presenter in BrowseActivity using dagger injection and method setPresenter() which I think would be redundant becouse of using dagger. Maybe you can correct me, if I'm mistaken.

Also I'm pretty excited about this article with new vision of clean architecture, and your second topic with new architecture components from google is good too. Nice work!

Useless BrowseActivitySubComponent

Hello guys !!

I've seen that BrowseActivitySubComponent is not used and only makes the dependency injection with Dagger harder to understand.

Maybe we can remove this file.

Thanks for this amazing project.

Will the presentation models always be useless?

Hello there. I was thinking after seeing a lot of repositories that usually the presenter or viewModel maps the domain models to presentation (there are no UI models) but this has a lot of problems (I shouldn't reference any platform resource, jetpack compose, etc). Even thinking in multiplatform this can be a huge problem.

Then I found this repo where I can see that the UI is separated from presentation so this problem is solved. But then I thought that really the presentation models will be the same than domain models.

Why should I keep the presentation models instead of using domain models directly until I reach the UI layer?

Architecture question

According to this talk : https://www.youtube.com/watch?v=Nsjsiz2A9mg
Uncle bob says that a project's structure should not be MVC or MVP, MVVM...,
But this project top structure is mobile-ui/presentation/domain/data...,
Programmers cannot know what is this app doing at first glimpse, could you tell me what do you think about this? I am so confused. Thank you!!

[QUESTION] How to use this boilerplate properly?

Thank you for creating this awesome project and the lovely documentation and art!

I am worried about the best way to include or start a derived professional app using AL 2.0.

I know the licenses are compatible, but I would like to do it correctly and don't delete the docs.

So new developers that continue the project understand the architecture easily and check changes.

I want to avoid starting from scratch, so I would like to:

  • Move the license.txt and readme.md to a documentation folder and link to it from the new one.
  • Rename packages.
  • Include the buffer sample temporarily until I finish my tested alternative.
  • Delete buffer*-related code.
  • Create proper credits to Buffer in documentation folder or a better and recommended place.

I am going to use this project in my personal apps, but I am worried about apps for clients.

I am planning to contribute so guidelines about style and this topic would be appreciated.

How can I fetch the data from cache when fetching data from remote failed?

Hi, I'm new on Clean architecture and we are planning to convert our project(MVP) to Clean architecture and I'm making POC project from our code base and see how it goes for my team.

as the sample code, the data fetch from the cache only the cache not expired or cached in our case is fetch the data from cached when fetching the data from network failed whatever case.

Any ideas?

Gradle dependencies don't get warnings about new version

I have implemented the gradle dependency template from the project and after a while I noticed that you dont get prompted anymore about new versions. AS doesn't recognise the patterns since it is broken down to multiple variables. Is this something we will have to learn to ignore or is there a way to make it work again without reverting to hardcoded dependencies?

LastCacheTime will be updated even if data is accessed from CacheDataStore?

I think there is some problem here. If I understand it correctly when the factory.retrieveDataStore() returns the BufferooCacheDataStore then this code is just taking the data from the cache and again storing it in the cache and updating LastCacheTime.

This does not feel right. Is this intended behavior or some sort of issue?

Refreshing Coroutines,Flow, KMM,Compose

Well, let's start with Coroutines instead of RXJava2. Hey joe I got a job with your Caster course on Clean Architecture, so I would like to give back, now that have two years and half real working experience, if someone is in to review the code I am all in to contribute to your project, as I read on twitter you want to give it but do not have time to work on that.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.