Stop wasting your time, start using Clean Architecture

Recently clean architecture on Android became very popular topic. I believe everyone who has ever tried to make maintainable and testable application realizes that this is very hard without clear separation of every layer. Thanks to Uncle Bob’s Clean Architecture and it’s dependency rule (source code dependencies can only point inwards) we can make applications that are not only testable, but also fully independent on UI, database and framework. Let’s imagine, you can leave your business logic and change whatever you want (platform or source for your data), without rebuilding whole project. Sounds great! So, sooner or later (and probably sooner) you’ll need to implement this few simple rules in your application. But it’s not always so obvious how to do it, especially for beginners. And here comes Rosie!

Rosie – the only woman you need

Who is this Rosie? And why is she here a superhero? Let me explain.

Rosie is an Android framework to help you create application that follows principles of Clean Architecture. It separates layers from each other and creates communication between them. So here we have:

View

Here comes all UI – activities, fragments, views and adapters for them. No business logic is here, only things that are necessary for showing app to the user. Rosie offers RosieActivity and RosieFragment for this layer.

Presenter

It’s the intermediate layer between UI and business logic. This layer is known from Model-View-Presenter pattern and it communicates directly View with Domain layer. It provides data to be shown on view with RosiePresenter.

Domain

Domain is the proper layer for business logic. Here we should put all use cases and business rules. It’s place for list of possible scenarios (e.g. getAllProducts(), or addPersonToList()). It’s important to not put Android dependencies in our code from this moment. All imports in Domain and Repository layer (and ideally in Presenter, but it’s not always possible) should come from pure Java. Rosie uses command patterns to execute use cases and have special RosieUseCase classes to help with creating use cases.

Repository

Repository is layer to encapsulate entities, data and business objects. We can implement different Repositories for API, Cache, Database, Cloud, or any other data source. The main rule is that Domain layer should not know from which source the data is taken – it shouldn’t matter. Rosie offers RosieRepository to handle few data sources, e.g. ReadableDataSource, WriteableDataSource, CacheDataSource.

Using Rosie

For each layer Rosie provides their own classes. For example, we create RosieActivity with our UI, which is supported by RosiePresenter. When our RosiePresenter wants to retrieves data, he creates call to one of the RosieUseCase. RosieUseCase make request to RosieRepository to get some data and RosieRepository decides if take data from cache (CacheDataSource) or e.g. from API (ReadableDataSource).

That’s the simplest case of using Rosie. It’s great way to organize whole application, even if we haven’t done anything with Clean Architecture. We can also implement pagination (by using PaginatedDataSource), we can use Dagger, ButterKnife and Renderers (last one is helpful when we want to use pagination). We can make global error handling (e.g. we can catch all errors and show them on a view).

Rosie – is this the real life? Is this just fantasy?

After all this great things I’ve read about Rosie I decided to try it. I created a small app using The Star Wars API (swapi.co). I made everything very similar to application from Rosie’s sample. Implementation was quite easy, but during this I found small issues that bother me:

Unfortunately, Rosie uses Dagger 1, instead of Dagger 2 for Dependency Injection. It’s worse for performance (making graph at runtime, using reflection) and for code readability (it’s hard to read generated code). Fortunately, I saw that this is one of the issues on GitHub, so there’s a chance to change this. RosieActivity, which we should use for our activities in project, extends from FragmentActivity. This class is good when we want to use Fragments and Loaders. But when we want to use support library action bar features, we should use AppCompatActivity. So why Rosie can’t use this class instead of FragmentActivity? It’ll be easier for everyone.

While making app, I’ve tried also to create some tests, because that’s the main reason I’ve decided to try Rosie. Unfortunately, also here I noticed a few things that made inconvenience (code not possible to execute is annotated)

While testing presenter you can’t mock your view in easy way. I wanted to call some methods from presenter and verify with Mockito if proper methods on view were called, unfortunately setter for view is package-private and bindings presenter with activity is made with reflection. Hard to test.

public class MainPresenterTest { @Mock UseCaseHandler useCaseHandler; @Mock GetPeople getPeople; private MainPresenter mainPresenter; @Test public void testUpdate_emptyList() throws Exception { //given PaginatedCollection<Person> emptyList = PaginatedCollectionUtils.createPaginatedCollection(new ArrayList<>()); when(getPeople.getAllPeopleInCache()).thenReturn(emptyList); //when mainPresenter.update(); //then verify(getPeople).getAllPeopleInCache(); verify(mainPresenterView).showPeople(emptyList.getItems()); verify(mainPresenterView).showHasMore(emptyList.hasMore()); } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public class MainPresenterTest { @Mock UseCaseHandler useCaseHandler ; @Mock GetPeople getPeople ; private MainPresenter mainPresenter ; @Test public void testUpdate_emptyList ( ) throws Exception { //given PaginatedCollection <Person> emptyList = PaginatedCollectionUtils . createPaginatedCollection ( new ArrayList < > ( ) ) ; when ( getPeople . getAllPeopleInCache ( ) ) . thenReturn ( emptyList ) ; //when mainPresenter . update ( ) ; //then verify ( getPeople ) . getAllPeopleInCache ( ) ; verify ( mainPresenterView ) . showPeople ( emptyList . getItems ( ) ) ; verify ( mainPresenterView ) . showHasMore ( emptyList . hasMore ( ) ) ; } Testing use cases is problematic since methods annotated with @UseCase use reflection. If we want to mock listener to onSuccess or onError, we can’t do it with Mockito.

public class GetPeopleTest { private GetPeople getPeople; @Test public void testGetPeople() throws Exception { //when getPeople.getPeople(Page.withOffsetAndLimit(0, 0)); //then verify(listener).onSuccess(); } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 public class GetPeopleTest { private GetPeople getPeople ; @Test public void testGetPeople ( ) throws Exception { //when getPeople . getPeople ( Page . withOffsetAndLimit ( 0 , 0 ) ) ; //then verify ( listener ) . onSuccess ( ) ; } } Testing presenter in full isolation of UseCaseHandler is impossible due to RosiePresenter’s method createUseCaseCall() (it’s method for calling our use case annotated with @RosieUseCase). It’s a big impediment that there are no tests for Presenter/Domain/Repository layer for sample app on Rosie’s GitHub.

Conclusion

After using Rosie for a while I can say that I’ll give it one more try and implement this framework in one of my applications. But is Rosie a must-have for every application? No. Rosie is great for beginners to dive into clean architecture and to see how each layer should be separated, but for bigger application I think it’s better to implement own version (maybe based on Rosie) to fit their needs. Even so, I recommend everyone to try on their own with Rosie.