Android Architecture: Part 3 – Applying clean architecture on Android

So far in this series, we’ve covered some beginner’s mistakes and gone through the clean architecture. In this last part, we will cover the last piece of the puzzle: labels, or more precisely, components.

First, I’ll remove the stuff we don’t use on Android projects and I’ll add some stuff that we do use but that isn’t found in the original Uncle Bob’s diagram. It looks like this:

I’ll go from the most abstract center to the edges.

Entities

Entities, aka domain objects or business objects, are the core of the app. They represent the main functionality of the app, and you should be able to tell what the app is about just by looking at them. They contain business logic, but it’s constrained to them only—validation and stuff like that. They don’t interact with the gritty outside-world details, and they also don’t handle persistence. If you had a news app, the entities would be Category, Article, and Commercial, for example.

Use cases

Use cases, aka interactors, aka business services, are an extension of the entities, an extension of the business logic, that is. They contain the logic that isn’t constrained only to one entity but handle more of them. An indicator of a good use case is that you can describe what it does in a simple sentence using common language—for example, “Transfer money from one account to another.” You can even use such nomenclature to name the class, e.g., TransferMoneyUseCase.

Repositories

Repositories serve to persist the entities. It’s as simple as that. They are defined as interfaces and serve as output ports for the use cases that want to perform CRUD operations on entities. Additionally, they can expose some more complex operations related to persistence such as filtering, aggregating, and so on. Concrete persistence strategies, e.g., database or the Internet, are implemented in the outer layers. You could name the interface AccountRepository, for instance.

Presenters

Presenters do what you would expect them to do if you are familiar with the MVP pattern. They handle user interactions, invoke appropriate business logic, and send the data to the UI for rendering. There is usually some mapping between various types of models here. Some would use controllers here, which is fine. The presenter we use is officially called the supervising controller. We usually define one or two presenters per screen, depending on the screen orientation, and our presenters’ lifecycles are tied to that of a view. One piece of advice: try to name your methods on a presenter to be technology agnostic. Pretend that you don’t know what technology the view is implemented in. So, if you have methods named onSubmitOrderButtonClicked and onUserListItemSelected in the view, the corresponding presenter methods that handle those events could be named submitOrder and selectUser.

Device

This component has already been teased before in the notifications (again) example. It contains the implementations of the gritty Android stuff such as sensors, alarms, notifications, players, all kinds of *Managers, and so on. It is a two-part component. The first part is the interfaces defined in the inner circles that business logic uses as the output port for communication with the outer world. The second part, and that’s drawn in the diagram, are implementations of those interfaces. So, you can define, for example, interfaces named Gyroscope, Alarm, Notifications, and Player. Notice that the names are abstract and technology agnostic. Business logic doesn’t care how the notification will be shown, how the player will play the sound, or where the gyroscope data comes from. You can make an implementation that writes the notifications to the terminal, write the sound data to the log, or gather the gyroscope data from the predefined file. Such implementations are useful for debugging or creating a deterministic environment for you to program in. But you will, of course, have to make implementations such as AndroidAlarm, NativePlayer, etc. In most cases, those implementations will just be wrappers around Android’s manager classes.

DB & API

No philosophy here. Put the implementations of repositories in this component. All the under-the-hood persistence stuff should be here: DAOs, ORM stuff, Retrofit (or something else) stuff, JSON parsing, etc. You can also implement a caching strategy here or simply use in-memory persistence until you are done with the rest of the app. We had an interesting discussion in the team recently. The question was this: Should the repository expose methods such as fetchUsersOffline (fetchUsersFromCache) and fetchUsersOnline (fetchUsersFromInternet)? In other words, should business logic know where the data comes from? Having read everything from this post, the answer is simple: no. But there is a catch. If the decision about the data source is part of the business logic—if the user can choose it, for example, or if you have an app with explicit offline mode—then you CAN add such a distinction. But I wouldn’t define two methods for every fetch. I would maybe expose methods such as enterOfflineMode and exitOfflineMode on the repository. Or if it applies to all the repositories, we could define an OfflineMode interface with enter and exit methods and use it on the business logic side, leaving repositories to query it for the mode and decide it internally.

UI

Even less philosophy here. Put everything related to the Android UI here. Activities, fragments, views, adapters, etc. Done.

Modules

The following diagram shows how have we divided all these components into Android Studio modules. You might find another division more appropriate.

graph-2

We group entities, use cases, repositories, and device interfaces into the domain module. If you want an extra challenge with a reward of eternal glory and a totally clean design, you can make that module a pure Java module. It will prevent you from taking shortcuts and putting something related to the Android here.

The device module should have everything related to Android that’s not data persistence and UI. The data module should hold everything related to data persistence, as we’ve already said. You cannot make those two into Java modules because they need access to various Android stuff. You can make them into Android library.

Finally, we group everything related to the UI (including presenters) into the UI module. You can explicitly name it UI but because of all the Android stuff here, we leave it named “app,” just as Android Studio named it during the creation of the project.

Is it better?

To answer that question, I’ll ditch Uncle Bob’s diagram and sprawl the components described before into a diagram like those we have used to rate previous types of architectures. After doing that, we get this:

graph-3

Now let’s apply the same criteria that we have used on previous architectures.

graph-4

It’s all neatly separated by module level, package level, and class level. So SRP should be satisfied.

graph-5

We have pushed Android and the real-world stuff as far out on the outskirts as we can. Business logic doesn’t touch the Android directly anymore.

graph-6

We have nicely separated classes that are easy to test. Classes touching the world can be tested using Android test cases; the one not touching it can be tested using JUnit. Someone malevolent would maybe call that class explosion. I call it testable. :)

graph-7

It may be complicated – but it’s worth it

I hope that my carefully chosen criteria, which was not made up just to favor the clean architecture, will convince you to give it a try. It seems complicated, and there are lots of details here, but it’s worth it. Once you wire it all up, testing is much easier, bugs are easier to isolate, new features are easy to add, code is more readable and maintainable, everything works perfectly, the universe is satisfied.

So, this is it. If you still haven’t done so, look at the previous articles in the series: Mistakes and Clean Architecture. If you have any comments or questions, leave feedback below. We are always interested in hearing what you think.

 

  • Pingback: Android Architecture: Part 2 - the clean architecture • Five()

  • Waleed sarwar

    I think i have read every blog related to Clean Architecture for Android on Internet.But So far this is one of best explanation of Clean Architecture in Android.

    • Tomo

      Thanks :)

  • Pingback: Swift Tutorial: An Introduction to the MVVM Design Pattern - Cible()

  • Pingback: Best Writing Service()

  • Patrick Egashira

    So far the best article i have read. Thank you for sharing! please keep writing.. i still havent found a better way for handling multiple views, for example an activity housing login and register fragments. i would appreciate a write up on this :)

    • Tomo

      Thank you very much :) Yes, activities, fragments, presenters and life-cycles. That’s a bit painful area :) We tried several approaches. The constant that remained the same is that we treat fragments as views, so presenters are tied to fragments. We treat activity just as a host for fragments. Currently the presenters are strongly coupled to fragments’ life-cycles, so when fragment goes to sleep, his presenter does so too, when fragment dies, we bury the presenter too. That introduces some problems when rotating the screen for example, because we cancel all the requests when rotating and reestablishing the connection again when showing rotated fragment. That’s good from the theoretical perspective, less side-effects, but users don’t actually care about side-effects :) But some of the guys in the team made some pretty nice RxJava sorcery that solved that problem and it behaves ok.

      • Suresh Joshi

        @disqus_6YW6LsKuLH:disqus Have you experimented at all with using MVVM instead of MVP for the UI layer? I’m more familiar with MVVM from my C# and Angular time – and I feel like it -could- work with all the data-binding we have access to now… Just haven’t tried it. Will be testing on an upcoming project.

        • Patrick Egashira

          Please let us know how the pieces fit in i could leverage my MVVM skills in this case too.

          • Tomo

            I’ve also tried MVVM with Xamarin and it works fine. I see no reason why it couldn’t be applied here. The roles are a bit reversed, data flows a bit differently, but the key is in both approaches imho to clearly hide the view behind the interface to be able to switch it and/or mock it. Both approaches work then. And that’s only UI layer, the rest of the layers are indifferent to you using MVVM, MVP, MVC , test environment, console, or PHP :) Kidding for last one, don’t go there :)

  • Pedro

    Nice overview on Clean. I think you nailed down its intent. Do you mind to explain how you handle models in this architecture? I’m interested to know if you have different models and mappers per layer.

    • Tomo

      Thanks. Yes, we have different models, but I didn’t want to make it even more complex here, it think it would draw attention from the more important stuff. I mentioned here only business models, or entities, but in reality we also have db models that ORM uses, and api models that Gson uses to fetch the data from the server. Because of that we also have DbMappers and APiMappers, that are usually very simple classes. If you are using Kotlin and / or RxJava, you can design mappers to use their map functions. Some methods become one liners in that a way.

      • Suresh Joshi

        For the mapper discussion, I can’t recall where I saw it, but I liked (but have never used) a pattern where there was a Mapper interface accepting templates – and at every level (data, domain, device, ui) there were a bunch of these mappers with 1-2 methods on them.

        Felt like overhead at the time, but then I realized I’m doing that already today in my designs – except instead of using a generic Mapper – I’m tightly coupling models via static methods or extensions.

        • Tomo

          We have an interface, but not so granular. Ours are a bit larger, have couple of methods. But yeah, this sounds good. If you saw that in some article feel free to post it.

  • narendra techguy

    Waiting for 4th part where i could see real code it would be better if you show it using some complex project that does not have simple usecases

    • Tomo

      The repo with real code is slowly loading :)

  • Suresh Joshi

    Easily the best practical explanation of Clean – would you happen to have an associated Github repo?
    Also, in the onion drawing – you’ve grouped Repos into the domain, whereas in one of the later drawings, the Repo is part of Data. Is that to specify a concrete implementation?

    • Tomo

      Thanks, there will be a repo soon. Good catch on Repositories. Well, same as devices, they are two-part. Repository interfaces are defined in the domain, because business logic talks only to that interfaces, and concrete implementations are defined in the Data module, just as you said.