Ingredients for a healthy codebase

6cd7230b4e87700f07a7bd5d28c54eb3?s=47 Romain Piel
November 10, 2015

Ingredients for a healthy codebase

Your app lives around monolithic Activities/Fragments? You think that singletons are amazing or you can’t get away from them? You find your app hard to test?

Writing robust and maintainable software is complex and we know that designing the general architecture of an app is complicated. The goal of this talk is to give you ingredients to build a healthy codebase for your Android app. By going through the entire stack from the data layer to the presentation layer, we will see how to make it maintainable and testable.

6cd7230b4e87700f07a7bd5d28c54eb3?s=128

Romain Piel

November 10, 2015
Tweet

Transcript

  1. Ingredients for a 
 healthy codebase Romain Piel

  2. @_rpiel Romain Piel at Songkick

  3. Songkick

  4. Architecture of an Android app?

  5. Let me tell you a story…

  6. John No knowledge in Android dev

  7. Let’s build an Android app ! John No knowledge in

    Android dev
  8. John MyActivity ApiManager

  9. John MyActivity ApiManager MyActivity

  10. John MyActivity ApiManager MyApplication MyActivity

  11. John MyActivity ApiManager MyApplication MyActivity DbManager

  12. Mary New hire !

  13. Mary This codebase looks so old! Let’s add change a

    few things… New hire !
  14. MyActivity ApiManager MyApplication MyActivity DbManager Mary John

  15. MyActivity ApiManager MyApplication MyActivity DbManager Retrofit Mary John

  16. MyActivity ApiManager MyApplication MyActivity DbManager Retrofit Mary NewFeature Activity NewFeature

    Fragment John
  17. MyActivity ApiManager MyApplication MyActivity DbManager Retrofit Mary NewFeature Activity NewFeature

    Fragment RxJava John
  18. App Mary John

  19. App Mary John

  20. App Mary John No idea where this crash is coming

    from
  21. Francis New hire !

  22. OMG ! Zero test ?! Francis New hire !

  23. Unit tests App Mary John Francis

  24. Unit tests App Mary John Francis Hard to catch up

    with the coverage & too many Android dependencies
  25. App Mary Francis UI tests John

  26. App Mary Francis UI tests Interactions with outside world =

    flaky tests John
  27. Mary John Francis App

  28. Mary John Francis App 1 year later…

  29. App

  30. App Dependant

  31. App Dependant • UI and business logic

  32. App Dependant • UI and business logic • Codebase and

    external libs
  33. App Dependant Hard to test

  34. App Dependant Hard to test Hard to maintain

  35. Another approach

  36. Systems should be • Independent of Frameworks • Testable •

    Independent of UI • Independent of Database • Independent of any external agency Clean architecture
  37. Let’s consider this problem

  38. None
  39. None
  40. Data layer

  41. Domain layer Data layer

  42. Presentation layer Domain layer Data layer

  43. Data layer Domain layer Data layer

  44. Data layer Domain layer Data layer

  45. Data layer : repository pattern ArtistRepository Retrofit client Db client

  46. Domain layer Presentation layer Domain layer Data layer

  47. Domain layer

  48. Domain layer • Orchestrates the flow of data with “use

    cases”
  49. Domain layer • Orchestrates the flow of data with “use

    cases” • Offers its services to the presentation layer
  50. Domain layer • Orchestrates the flow of data with “use

    cases” • Offers its services to the presentation layer • Pure Java module • No Android UI dependencies • No dependency to external source (db, content provider, shared preferences…)
  51. Presentation layer <activity android:name=".SearchActivity"/>

  52. <activity android:name=".SearchActivity"/>

  53. <activity android:name=".SearchActivity"/> • Tied to the view lifecycle

  54. <activity android:name=".SearchActivity"/> • Tied to the view lifecycle • Receives

    user inputs
  55. <activity android:name=".SearchActivity"/> • Tied to the view lifecycle • Receives

    user inputs • Associated to specific view layouts (ListActivity)
  56. <activity android:name=".SearchActivity"/> • Tied to the view lifecycle • Receives

    user inputs • Associated to specific view layouts (ListActivity) Your very first Android Activity will be likely to be huge and untestable
  57. View Presentation layer Domain layer Data Interaction

  58. View Presentation layer Domain layer Data Interaction

  59. [ ] View Presentation layer Domain layer Data Interaction

  60. Presentation layer View Presenter Model Data Interaction

  61. View Presenter Model Presentation layer interface SearchPresenter {
 void searchArtist(String

    searchTerm);
 void clickArtist(Artist artist);
 } interface SearchView {
 void showProgress();
 void hideProgress();
 void showArtists(List<Artist> artists);
 }
  62. Model vs. View model class Artist {
 String displayName;
 String

    uri;
 String id;
 LocalDate onTourUntil;
 }
  63. class Artist {
 String displayName;
 String uri;
 String id;
 LocalDate

    onTourUntil;
 } Model vs. View model
  64. class ArtistViewModel {
 String name; boolean isOnTour; } Model vs.

    View model
  65. class ArtistViewModel {
 String name; boolean isOnTour; } public class

    ArtistViewHolder extends ViewHolder {
 
 @Bind(R.id.artist_name)
 TextView artistName; @Bind(R.id.on_tour)
 TextView onTour;
 
 public ArtistViewHolder(View itemView) {
 super(itemView);
 ButterKnife.bind(this, itemView);
 }
 
 @Override
 public void bind(ArtistViewModel viewModel) {
 artistName.setText(artistViewModel.name);
 onTour.setVisibility(artistViewModel.isOnTour ? View.VISIBLE : View.GONE );
 }
 }
  66. Presentation layer interface SearchPresenter {
 void searchArtist(String searchTerm);
 void clickArtist(ArtistViewModel

    artist);
 } interface SearchView {
 void showProgress();
 void hideProgress();
 void showArtists(List<ArtistViewModel> artists);
 } View Presenter View model
  67. Presentation layer class SearchFragment extends Fragment implements SearchView {
 


    SearchPresenter searchPresenter;
 
 @Override
 void showProgress() {
 // ...
 }
 
 @Override
 void hideProgress() {
 // ...
 }
 
 @Override
 void showArtists(List<ArtistViewModel> artists) {
 // ...
 } {...}
 }
  68. Presentation layer class SearchFragment extends Fragment implements SearchView {
 


    SearchPresenter searchPresenter;
 {...} 
 void onItemClick(ArtistViewModel artist) {
 searchPresenter.clickArtist(artist); }
 
 void search(String searchTerm) {
 searchPresenter.searchArtist(searchTerm); }
 }
  69. Presentation layer Domain layer Data layer

  70. Communication Presentation layer Domain layer Data layer Subscriber<ViewModel> Observable<ViewModel> Observable<Model>

    Rx all the things!
  71. interface ArtistRepository {
 Observable<List<Artist>> search(String searchTerm);
 } Data layer Communication

    Rx all the things!
  72. interface ArtistRepository {
 Observable<List<Artist>> search(String searchTerm);
 } Domain layer Data

    layer interface SearchArtistUseCase<VM extends ViewModel> {
 void subscribe(Subscriber<VM> subscriber); void unsubscribe();
 } Communication Rx all the things!
  73. interface ArtistRepository {
 Observable<List<Artist>> search(String searchTerm);
 } Presentation layer Domain

    layer Data layer interface SearchArtistUseCase<VM extends ViewModel> {
 void subscribe(Subscriber<VM> subscriber); void unsubscribe();
 } interface SearchPresenter {
 void searchArtist(String searchTerm); void onDestroy();
 } Communication Rx all the things!
  74. Structure Dagger

  75. Structure Application
 component Dagger

  76. Structure Application
 component Application modules Repositories Dagger

  77. Structure Application
 component Activity component Application modules Repositories Dagger

  78. Structure Application
 component Activity component Application modules Repositories Activity modules

    Activity Dagger
  79. Structure Application
 component Activity component Application modules Repositories Fragment modules

    Presenter Use cases Activity modules Activity Dagger
  80. Structure

  81. Structure “Program to an interface, not an implementation” — Erich

    Gamma
  82. Structure “Program to an interface, not an implementation” — Erich

    Gamma • Decouple the client from the implementation
  83. Structure “Program to an interface, not an implementation” — Erich

    Gamma • Decouple the client from the implementation • Defines the vocabulary of the collaboration
  84. Structure “Program to an interface, not an implementation” — Erich

    Gamma • Decouple the client from the implementation • Defines the vocabulary of the collaboration • Easy to test
  85. Testing Presentation layer Domain layer Data layer Unit SearchUseCase ArtistRepository

    SearchPresenter SearchFragment
  86. Testing Presentation layer Domain layer Data layer Unit SearchUseCase ArtistRepository

    SearchPresenter SearchFragment Testable without Robolectric
  87. Testing Presentation layer Domain layer Data layer Unit SearchUseCase ArtistRepository

    SearchPresenter SearchFragment Testable without Robolectric Testable with Robolectric
  88. Testing Presentation layer Domain layer Data layer UI

  89. Testing Presentation layer Domain layer Data layer UI .json

  90. Testing Presentation layer Domain layer Data layer UI .json class

    ArtistRepositoryTestImpl {
 
 JsonLoader jsonLoader;
 
 Observable<List<Artist>> search(String searchTerm) {
 return jsonLoader.load(“search/artist.json”);
 }
 }
  91. Conclusion

  92. Conclusion • Choose an architecture and stick with it

  93. Conclusion • Choose an architecture and stick with it •

    Test while you code
  94. Conclusion • Choose an architecture and stick with it •

    Test while you code • Always do what’s best for the codebase, not for the feature you’re implementing
  95. Songkick We’re hiring!

  96. Links • Uncle Bob’s clean architecture 
 http://blog.8thlight.com/uncle-bob/2012/08/13/the-clean-architecture.html
 • Fernando

    Cejas - Architecting Android…The clean way?
 http://fernandocejas.com/2014/09/03/architecting-android-the-clean-way
 https://github.com/android10/Android-CleanArchitecture
 • Martin Fowler - The repository pattern
 http://martinfowler.com/eaaCatalog/repository.html
 • Erich Gamma - Design Principles from Design Patterns
 http://www.artima.com/lejava/articles/designprinciples.html
  97. Thank you