Through this presentation you will gain a good understanding of how the clean architecture pattern is implemented at Taxibeat. What issues the Android Taxibeat team has faced so far and what solutions we came up with. Of course, the benefits of clean architecture will also be discussed along with the way we managed to build two fast paced iterative apps that share functionality.
3. Once upon a time…
• Driver and passenger apps started as two
simple projects with the goal of providing an
easy transportation platform for the people
• There was a basic structure and some simple
features
• The need for expanding on different markets
and tense competition forced quick changes to
this structure making the code complex
4. Complex meaning…
• God Activities
• Humongous helper/manager classes that became extra
sophisticated
• Legacy code
• The code responsible for providing the UX
(business rules) was tightly coupled with the
code responsible for the UI
5. App Redesign
• A new architecture should be able to:
• separate UI from UX
• let as develop and debug faster
• let as code on the same “activity” at the same time with the least
of conflicts
• provide a set of rules that new members of the team should follow
so as to learn the codebase faster
• be test friendly
• A pattern like MVP was not enough
7. Clean architecture to the
rescue
• It is not a pattern like MVP
• It is a set of rules in order to create a layered
approach. These layers are able to separate
the platform specific code from the business
rules
I want you to think of your application as a group of use
cases that describe the intent of the application and a
group of plugins that give those use cases access to the
outside world.
Robert C Martin “Uncle Bob”
10. Presentation Layer
• This layer provides the UI and a great deal of the UX
to the user
Android
Component
Presenter
Screen
Interface
• Android Components are the
Activities, Services,
Fragments, Notifications
etc…
• The Screen is an Interface
that defines the methods
that the Android Component
should implement
• The Presenter provides the
business logic
Read also here: https://medium.com/taxibeat/android-architecture-with-multi-screen-
mvp-46d3ccafa7b9
11. Presentation Layer in practise
public class ExampleActivity extends AppCompatActivity
implements ExampleScreen {
private ExamplePresenter presenter;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.act_example);
presenter = new ExamplePresenter(this);
presenter.initialize();
}
@Override
public void showLoading() { /* Actual Implementation */ }
@Override
public void hideLoading() { /* Actual Implementation */ }
@Override
public void showData(Data data) { /* Actual Implementation */ }
@Override
public void showError(String error) { /* Actual Implementation */ }
}
public interface ExampleScreen {
void showLoading();
void hideLoading();
void showData(Data data);
void showError(String error);
}
The screen does not
know who implements
its methods
It may be an activity, a
fragment, a notification
etc…
In this case it’s an
activity
12. Presentation Layer in practise
public class ExamplePresenter {
private ExampleScreen screen;
public ExamplePresenter(ExampleScreen screen) {
this.screen = screen;
}
public void initialize() {
getData();
}
void getData() {
screen.showLoading();
BusProvider.getUIBusInstance().register(this);
new GetDataUseCase(new WebDataRepository()).execute();
}
@Subscribe
public void onGetDataResponse(Data data) {
BusProvider.getUIBusInstance().unregister(this);
screen.hideLoading();
screen.showData(data);
}
@Subscribe
public void onGetDataError(Error error) {
BusProvider.getUIBusInstance().unregister(this);
screen.hideLoading();
screen.showError(error.getMessage());
}
}
public interface ExampleScreen {
void showLoading();
void hideLoading();
void showData(Data data);
void showError(String error);
}
The presenter is
communicating solely
with the screen and
not with the Android
Component
13. Domain Layer
• This layer orchestrates the data flow
Presenter
Use Case
Model
DataSource
Interface
Main (UI) Bus Provider
Domain Layer
Rest Bus Provider
14. Domain Layer in practise
public interface GetDataSource {
void getData();
}
public class Data implements Serializable {
private String dataStr;
public String getDataStr() {
return dataStr;
}
public void setDataStr(String dataStr) {
this.dataStr = dataStr;
}
}
public class GetDataUseCase {
private GetDataSource dataSource;
public GetDataUseCase(GetDataSource dataSource) {
this.dataSource = dataSource;
}
@Subscribe
public void onGetDataResponse(Data data) {
BusProvider.getUIBus().post(data);
BusProvider.getRestBus().unregister(this);
}
@Subscribe
public void onGetDataError(Error error) {
BusProvider.getUIBus().post(error);
BusProvider.getRestBus().unregister(this);
}
public void execute() {
BusProvider.getRestBus().register(this);
dataSource.getData();
}
}
There is no reference
neither to the activity nor
to the “Retrofit code”
15. Data Layer
• This layer retrieves the data
DataSource
Interface
Domain Layer
Rest Bus Provider
WebDataRepository
DBDataRepository
CachedDataReposit
ory
Data
Mapper
Data layer
16. Data Layer in practise
public class WebDataRepository implements GetDataSource {
private DataClient client;
public WebDataRepository() {
client = Retrofit.createClient(DataClient.class);
}
@Override
public void getData() {
Call<DataEntity> call = client.getDataFromWeb();
call.enqueue(new Callback<DataEntity>() {
@Override
public void onResponse(Call<DataEntity> call,
Response<DataEntity> response) {
Data data = new DataMapper().transform(response.body());
BusProvider.getRestBusInstance().post(data);
}
@Override
public void onFailure(Call<DataEntity> call, Throwable t) {
Error error = new ErrorMapper().transform(t);
BusProvider.getRestBusInstance().post(error);
}
});
}
}
public class DataMapper {
public Data transform(
DataEntity entity) {
Data data = new Data();
data.setDataStr(
String.valueOf(entity.getInt()));
return data;
}
}
Here is written the
implementation
detail
The mapper is a safe
mechanism to transform
the response to the data
we actually need
17. Why all the fuss?
• Our use cases need to implement the following mechanisms:
1. Reauthorisation
2. Caching
3. Server downtime handling
4. In App broadcasts
5. Redirect
6. Different geocoding services
• Presenters need to listen and show those In-Apps or handle redirect links
(Opening the appropriate activity)
18. Why all the fuss?
Feature Presenter
Base Presenter
Feature Use Case
Base Use Case
Repository
• Re-login
• Server Downtime
• In App Broadcasts
• Redirect
• Check reverse
geocoding results
• Show In App
Dialogs
• Handle redirect
navigation
19. Conclusion
• By following the rules of Clean Architecture we split the core
features’ code from the front end code.
• We can mock our rest responses and develop new features in
parallel with the server side development
• All the modules are testable.
• Newcomers can focus to the business side of the code and are
not lost on the plugin details, while learning the codebase.
• On the downside we have to write more code than we used to. To
combat that we created code templates inside Android Studio!
23. Resources
• Uncle Bob
The original blog post: https://8thlight.com/blog/uncle-bob/2012/08/13/the-clean-architecture.html
Talk: https://www.youtube.com/watch?v=Nsjsiz2A9mg
• Fernando Cejas
Sample app written with the clean architecture approach using Dagger and RxJava https://
github.com/android10/Android-CleanArchitecture
• Tomislav Homan
A series of articles that detail the process of migrating Five Agency’s apps to clean architecture
http://five.agency/android-architecture-part-1-every-new-beginning-is-hard/
• Chryssa Aliferi
Article series about the MVP @ Taxibeat https://medium.com/taxibeat/android-architecture-with-
multi-screen-mvp-46d3ccafa7b9
Presentation https://speakerdeck.com/chryssaaliferi/android-mvp-pattern-and-multi-presenter-
activities