Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Refactoring Wunderlist for Android - Episode I. The presentation layer -

Refactoring Wunderlist for Android - Episode I. The presentation layer -

In this talk, we will go through an overview of how Wunderlist was built and how we are now refactoring the code base to make it better, more decoupled, more testable, easier to extend, and easier to maintain.

In this 1st chapter of refactoring Wunderlist, we will go through the MVP pattern used to decouple the different elements of the view, and I will show also the different approaches we have taken for fetching data, till the one we've finally chosen, using reactive programming.

9fa66713006f997a5c658adabd2add25?s=128

Cesar Valiente

April 16, 2016
Tweet

Transcript

  1. Refactoring Wunderlist for Android César Valiente - Episode I. The

    presentation layer -
  2. Who is this guy? Image Placeholder César Valiente Android Engineer

    @Wunderlist (@Microsoft) Android Google Developer Expert (GDE) +CesarValiente @CesarValiente
  3. This is a story of…

  4. evolution

  5. growth evolution

  6. growth simplicity evolution

  7. growth simplicity evolution improvement

  8. How is Wunderlist built?

  9. How is Wunderlist built? Android Layer Presentation layer (UI and

    Android stuff) Android project
  10. How is Wunderlist built? Android Layer Presentation layer (UI and

    Android stuff) Android project Sync Layer Model layer (Business logic) Java project
  11. How is Wunderlist built? Android Layer Presentation layer (UI and

    Android stuff) Android project Sync Layer Model layer (Business logic) Java project Network Layer Network layer (Accessing to the API data) Java project
  12. How is Wunderlist built? Android Layer Presentation layer (UI and

    Android stuff) Android project Sync Layer Model layer (Business logic) Java project Network Layer Network layer (Accessing to the API data) Java project Sync boundaries
  13. How is Wunderlist built? Android Layer Presentation layer (UI and

    Android stuff) Android project Sync Layer Model layer (Business logic) Java project Network Layer Network layer (Accessing to the API data) Java project Sync boundaries Network boundaries
  14. Dependency rule

  15. Network Dependency rule

  16. Sync Network Dependency rule

  17. Presentation Sync Network Dependency rule

  18. Presentation Sync Network Dependency rule The outer model knows the

    inner, not viceversa.
  19. Problems?

  20. Problems? Activities/Fragments become GOD classes.

  21. Problems? Activities/Fragments become GOD classes. Responsibilities are messed up.

  22. Problems? Activities/Fragments become GOD classes. Responsibilities are messed up. Database

    in presentation layer (since it was our only Android related layer).
  23. Problems? Activities/Fragments become GOD classes. Responsibilities are messed up. Difficult

    to test: framework dependencies. Database in presentation layer (since it was our only Android related layer).
  24. Problems? Activities/Fragments become GOD classes. Responsibilities are messed up. Difficult

    to test: framework dependencies. Database in presentation layer (since it was our only Android related layer). Cache in the sync layer.
  25. Problems? Activities/Fragments become GOD classes. Responsibilities are messed up. Difficult

    to test: framework dependencies. Database in presentation layer (since it was our only Android related layer). Cache in the sync layer. Complex data schemes used also in the presentation layer, not needed here.
  26. Problems? Activities/Fragments become GOD classes. Responsibilities are messed up. Difficult

    to test: framework dependencies. Database in presentation layer (since it was our only Android related layer). Cache in the sync layer. Complex data schemes used also in the presentation layer, not needed here. We wanted to do it better!
  27. What are we going to do?

  28. What are we going to do? We are going to

    separate responsibilities. Yes, Even more!
  29. What are we going to do? We are going to

    separate responsibilities. Yes, Even more! We are going to decouple the different core elements of our app.
  30. What are we going to do? We are going to

    separate responsibilities. Yes, Even more! We are going to decouple the different core elements of our app. We are going to improve the way we fetch data.
  31. What are we going to do? We are going to

    separate responsibilities. Yes, Even more! We are going to decouple the different core elements of our app. We are going to improve the way we fetch data. We are going to make testing easier to do, and increase our test coverage.
  32. What are we going to do? We are going to

    separate responsibilities. Yes, Even more! We are going to decouple the different core elements of our app. We are going to improve the way we fetch data. We are going to make testing easier to do, and increase our test coverage. We are going to improve our codebase.
  33. What are we going to do? We are going to

    separate responsibilities. Yes, Even more! We are going to REFACTOR We are going to decouple the different core elements of our app. We are going to improve the way we fetch data. We are going to make testing easier to do, and increase our test coverage. We are going to improve our codebase.
  34. Presentation layer

  35. Presentation layer App/UI (Android project)

  36. Presentation layer App/UI (Android project) UI domain (Java project)

  37. Presentation layer App/UI (Android project) UI domain (Java project) DB/Cache

    (Android project)
  38. Everything starts with the Model View Presenter (MVP)

  39. VIEW Activity/Fragment Everything starts with the Model View Presenter (MVP)

  40. VIEW Activity/Fragment PRESENTER Man in the middle Everything starts with

    the Model View Presenter (MVP)
  41. VIEW Activity/Fragment PRESENTER Man in the middle MODEL Business logic

    Everything starts with the Model View Presenter (MVP)
  42. VIEW Activity/Fragment PRESENTER Man in the middle MODEL Business logic

    User interacts with the app Everything starts with the Model View Presenter (MVP)
  43. VIEW Activity/Fragment PRESENTER Man in the middle MODEL Business logic

    Starts the process User interacts with the app Everything starts with the Model View Presenter (MVP)
  44. VIEW Activity/Fragment PRESENTER Man in the middle MODEL Business logic

    Starts the process Executes use case User interacts with the app Everything starts with the Model View Presenter (MVP)
  45. VIEW Activity/Fragment PRESENTER Man in the middle MODEL Business logic

    Starts the process Executes use case Returns the result User interacts with the app Everything starts with the Model View Presenter (MVP)
  46. VIEW Activity/Fragment PRESENTER Man in the middle MODEL Business logic

    Starts the process Executes use case Updates view Returns the result User interacts with the app Everything starts with the Model View Presenter (MVP)
  47. None
  48. VIEW PRESENTER

  49. VIEW PRESENTER VIEWCALLBACK USE CASE

  50. VIEW PRESENTER VIEWCALLBACK USE CASE implements and passes it to

    the presenter
  51. VIEW PRESENTER VIEWCALLBACK USE CASE implements and passes it to

    the presenter creates an instance and passes it to the presenter
  52. VIEW PRESENTER VIEWCALLBACK USE CASE implements and passes it to

    the presenter creates an instance and passes it to the presenter will execute it to start the process
  53. VIEW PRESENTER VIEWCALLBACK USE CASE implements and passes it to

    the presenter creates an instance and passes it to the presenter will invoke it to communicate back (when we have the result) will execute it to start the process
  54. ViewCallback

  55. public interface ViewCallback {
 void showProgressDialog();
 void hideProgressDialog();
 void updateList(List

    content);
 }
  56. View

  57. public class SharingFragmentActivity extends WLFragmentActivity implements ViewCallback {

  58. private View progressDialog;
 private Adapter adapter;
 private SharingPresenter sharingPresenter;
 


    public void onCreate (Bundle onSavedInstanceState) {
 super.onCreate(onSavedInstanceState);
 
 bindViews();
 setupPresenters();
 } public class SharingFragmentActivity extends WLFragmentActivity implements ViewCallback {
  59. private View progressDialog;
 private Adapter adapter;
 private SharingPresenter sharingPresenter;
 


    public void onCreate (Bundle onSavedInstanceState) {
 super.onCreate(onSavedInstanceState);
 
 bindViews();
 setupPresenters();
 } public class SharingFragmentActivity extends WLFragmentActivity implements ViewCallback { private void setupPresenters () {
 GetListMembershipUseCase getListMembershipUseCase = new GetListMembershipUseCase( listId, appDataController); sharingPresenter = new SharingPresenter ( this, getListMembershipUseCase);
 } //——————— next slides ———————//
  60. None
  61. public class SharingFragmentActivity extends WLFragmentActivity implements ViewCallback { //——————————-— previous

    slide (setup) ———————————-—// //——————— next slide (presenter invocation) ———————//
  62. @Override void showProgressDialog() {
 progressDialog.show();
 }
 
 @Override void hideProgressDialog()

    {
 progressDialog.hide();
 }
 
 @Override void updateList(List<Memberships> list) {
 adapter.updateContent(list);
 }
 } public class SharingFragmentActivity extends WLFragmentActivity implements ViewCallback { //——————————-— previous slide (setup) ———————————-—// //——————— next slide (presenter invocation) ———————// implements ViewCallback {
  63. None
  64. //——————————— slide 1 (setup) —————————-—// public class SharingFragmentActivity extends WLFragmentActivity

    implements ViewCallback { //————— slide 2 (ViewCallback impl) ————-//
  65. @OnClick private void showListMembers () {
 sharingPresenter.getListMembers();
 } //——————————— slide

    1 (setup) —————————-—// public class SharingFragmentActivity extends WLFragmentActivity implements ViewCallback { //————— slide 2 (ViewCallback impl) ————-//
  66. Presenter

  67. public class SharingPresenter extends Presenter {

  68. public class SharingPresenter extends Presenter { private ViewCallback viewCallback;
 private

    GetListMembershipUseCase getListMembershipUseCase;
 
 public SharingPresenter ( ViewCallback viewCallback, GetListMembershipUseCase getListMembershipUseCase) { 
 this.viewCallback = viewCallback;
 this.getListMembershipUseCase = getListMembershipUseCase;
 } //——————— next slides ———————//
  69. None
  70. public class SharingPresenter extends Presenter { //——————————-— previous slide (setup)

    ———————————-—//
  71. @Override public void onStart() {
 EventBus.getDefault().register(this);
 }
 
 @Override public

    void onStop() {
 EventBus.getDefault().unregister(this);
 } public class SharingPresenter extends Presenter { //——————————-— previous slide (setup) ———————————-—//
  72. @Override public void onStart() {
 EventBus.getDefault().register(this);
 }
 
 @Override public

    void onStop() {
 EventBus.getDefault().unregister(this);
 } public class SharingPresenter extends Presenter { //——————————-— previous slide (setup) ———————————-—// public void onEventMainThread(ListMembershipEvent event) {
 viewCallback.hideProgressDialog();
 viewCallback.updateList(event.getContent());
 } //——————— next slide (Use Case execution) ———————//
  73. None
  74. public class SharingPresenter extends Presenter { //————————————- slide 1 (setup)

    ———————————————————-—// //———————————— slide 2 (listening for events) ———————//
  75. public void showListMembers () {
 viewCallback.showProgressDialog();
 getListMembershipUseCase.execute();
 }
 } public

    class SharingPresenter extends Presenter { //————————————- slide 1 (setup) ———————————————————-—// //———————————— slide 2 (listening for events) ———————//
  76. None
  77. M O D E L

  78. M O D E L Use Cases

  79. UI-Items, DB, Cache, etc. M O D E L Use

    Cases
  80. Use case

  81. public class GetListMembershipUseCase implements UseCase<List<WLMembership>> {

  82. public class GetListMembershipUseCase implements UseCase<List<WLMembership>> { private String listId;
 private

    AppDataController appDataController;
 
 public GetListUseCase( String listId, AppDataController appDataController) {
 this.listId = listId;
 this.appDataController = appDataController;
 }
 

  83. public class GetListMembershipUseCase implements UseCase<List<WLMembership>> { private String listId;
 private

    AppDataController appDataController;
 
 public GetListUseCase( String listId, AppDataController appDataController) {
 this.listId = listId;
 this.appDataController = appDataController;
 }
 
 @Override
 public void execute() {
 appDataController.get(ApiObjectType.MEMBERSHIP, listId);
 }
 }
  84. How were we fetching data?

  85. LOADER Activity/ Fragment DB Presentation layer 1. Using loaders

  86. LOADER Activity/ Fragment DB Presentation layer 1. Using loaders

  87. LOADER Activity/ Fragment DB Presentation layer 1. Using loaders

  88. LOADER Activity/ Fragment BUS Event or data is fired Presentation

    layer 2. Using loaders + Bus
  89. LOADER Activity/ Fragment BUS Subscription Event or data is fired

    Presentation layer 2. Using loaders + Bus
  90. LOADER Activity/ Fragment BUS Subscription Event or data is fired

    Data Presentation layer 2. Using loaders + Bus
  91. LOADER Activity/ Fragment BUS Subscription Data Event or data is

    fired Data Presentation layer 2. Using loaders + Bus
  92. And how do we fetch data now? (an evolution of

    approaches)
  93. 1. Use case that returns data. Synchronous. Presenter View Presentation

    layer Model UI domain db/ cache U s e C a s e
  94. 1. Use case that returns data. Synchronous. Presenter View Presentation

    layer Model UI domain db/ cache U s e C a s e 1
  95. 1. Use case that returns data. Synchronous. Presenter View Presentation

    layer Model UI domain db/ cache U s e C a s e 1 2
  96. 1. Use case that returns data. Synchronous. Presenter View Presentation

    layer Model UI domain db/ cache U s e C a s e 1 2 3
  97. 1. Use case that returns data. Synchronous. Presenter View Presentation

    layer Model UI domain db/ cache U s e C a s e 1 2 3 4
  98. 1. Use case that returns data. Synchronous. Presenter View Presentation

    layer Model UI domain db/ cache U s e C a s e 1 2 5 3 4
  99. 1. Use case that returns data. Synchronous. Presenter View Presentation

    layer Model UI domain db/ cache U s e C a s e 1 2 5 6 3 4
  100. 2. Use case using callback. Asynchronous. 2 Presenter View Presentation

    layer Model UI domain db/ cache U s e C a s e
  101. 2. Use case using callback. Asynchronous. 2 Presenter View Presentation

    layer Model UI domain db/ cache U s e C a s e 1
  102. 2. Use case using callback. Asynchronous. 2 Presenter View Presentation

    layer Model UI domain db/ cache U s e C a s e 1 2
  103. 2. Use case using callback. Asynchronous. 2 Presenter View Presentation

    layer Model UI domain db/ cache U s e C a s e 1 2 3
  104. 2. Use case using callback. Asynchronous. 2 Presenter View Presentation

    layer Model UI domain db/ cache U s e C a s e 1 2 3 4
  105. 2. Use case using callback. Asynchronous. 2 Presenter View Presentation

    layer Model UI domain db/ cache U s e C a s e 1 2 5 3 4
  106. 2. Use case using callback. Asynchronous. 2 Presenter View Presentation

    layer Model UI domain db/ cache U s e C a s e 1 2 5 6 3 4
  107. 3. Use case using an event bus. Presenter View Presentation

    layer Model UI domain db/ cache U s e C a s e BUS
  108. 3. Use case using an event bus. Presenter View Presentation

    layer Model UI domain db/ cache U s e C a s e 1 BUS
  109. 3. Use case using an event bus. Presenter View Presentation

    layer Model UI domain db/ cache U s e C a s e 1 2 BUS
  110. 3. Use case using an event bus. Presenter View Presentation

    layer Model UI domain db/ cache U s e C a s e 1 2 BUS 3
  111. 3. Use case using an event bus. Presenter View Presentation

    layer Model UI domain db/ cache U s e C a s e 1 2 BUS 3 4
  112. 3. Use case using an event bus. Presenter View Presentation

    layer Model UI domain db/ cache U s e C a s e 1 2 BUS 3 4 5
  113. 3. Use case using an event bus. Presenter View Presentation

    layer Model UI domain db/ cache U s e C a s e 1 2 6 BUS 3 4 5
  114. 4. Use case using RxJava Model UI domain db/ cache

    OBSERVABLE Presentation layer VIEW PRESENTER OBSERVER (USE CASE)
  115. 4. Use case using RxJava Model UI domain db/ cache

    OBSERVABLE Presentation layer VIEW PRESENTER OBSERVER 1 (USE CASE)
  116. 4. Use case using RxJava Model UI domain db/ cache

    OBSERVABLE Presentation layer VIEW PRESENTER OBSERVER 1 2 (USE CASE)
  117. 4. Use case using RxJava Model UI domain db/ cache

    OBSERVABLE Presentation layer VIEW PRESENTER OBSERVER 3 1 2 (USE CASE)
  118. 4. Use case using RxJava Model UI domain db/ cache

    OBSERVABLE Presentation layer VIEW PRESENTER OBSERVER 3 4 1 2 (USE CASE)
  119. 4. Use case using RxJava Model UI domain db/ cache

    OBSERVABLE Presentation layer VIEW PRESENTER OBSERVER 5 3 4 1 2 (USE CASE)
  120. 4. Use case using RxJava Model UI domain db/ cache

    OBSERVABLE Presentation layer 6 VIEW PRESENTER OBSERVER 5 3 4 1 2 (USE CASE)
  121. Testing?

  122. Testing? Views now don’t have business logic and/or data.

  123. Testing? Views now don’t have business logic and/or data. Presenters

    don’t have framework dependencies.
  124. Testing? Views now don’t have business logic and/or data. Presenters

    don’t have framework dependencies. Dependency injection make testing easier
  125. Testing? Views now don’t have business logic and/or data. Presenters

    don’t have framework dependencies. Use cases are very small and simple. Dependency injection make testing easier
  126. Testing? Views now don’t have business logic and/or data. Presenters

    don’t have framework dependencies. Use cases are very small and simple. We can test now every component independently. Dependency injection make testing easier
  127. Testing? Views now don’t have business logic and/or data. Presenters

    don’t have framework dependencies. Use cases are very small and simple. We can test now every component independently. Dependency injection make testing easier Now testing is easy peasy!
  128. More testing?

  129. More testing? VIEW Espresso Java + Android Mockito JUnit

  130. More testing? VIEW Espresso Java + Android Mockito JUnit PRESENTER

    TestSubscriber Mockito JUnit Java + RxJava
  131. More testing? VIEW Espresso Java + Android Mockito JUnit PRESENTER

    TestSubscriber Mockito JUnit Java + RxJava USE CASE TestSubscriber Mockito JUnit Java + RxJava
  132. More testing? VIEW Espresso Java + Android Mockito JUnit PRESENTER

    TestSubscriber Mockito JUnit Java + RxJava USE CASE TestSubscriber Mockito JUnit Java + RxJava UI- DOMAIN TestSubscriber Mockito JUnit Java + RxJava
  133. More testing? VIEW Espresso Java + Android Mockito JUnit PRESENTER

    TestSubscriber Mockito JUnit Java + RxJava USE CASE TestSubscriber Mockito JUnit Java + RxJava UI- DOMAIN TestSubscriber Mockito JUnit Java + RxJava DB/ CACHE Java + Android + RxJava Robolectric Mockito JUnit TestSubscriber
  134. Lessons learned

  135. Lessons learned Dependency Inversion Principle is key.

  136. Lessons learned Dependency Inversion Principle is key. Following SOLID principles

    makes MVP easy to build.
  137. Lessons learned Dependency Inversion Principle is key. Following SOLID principles

    makes MVP easy to build. Small use cases define what you want to do.
  138. Lessons learned Dependency Inversion Principle is key. Following SOLID principles

    makes MVP easy to build. Small use cases define what you want to do. We have different alternatives to fetch data, choose yours.
  139. Lessons learned Dependency Inversion Principle is key. Following SOLID principles

    makes MVP easy to build. Small use cases define what you want to do. We have different alternatives to fetch data, choose yours. Decouple components makes testing easier.
  140. ? +CesarValiente @CesarValiente

  141. +CesarValiente @CesarValiente Thanks!

  142. License (cc) 2016 César Valiente. Some rights reserved. This document

    is distributed under the Creative Commons Attribution-ShareAlike 3.0 license, available in http://creativecommons.org/licenses/by-sa/3.0/
  143. Image licenses Wunderlist (Microsoft): permission granted. Emojis by Emoji One

    (CC-BY): http://emojione.com/ Iceberg: http://science-all.com/iceberg.html All images belong to their owners.