You are on page 1of 97

Ingredients for a 


healthy codebase

Romain Piel
Romain Piel
at Songkick

@_rpiel
Songkick
Architecture of an
Android app?
Let me tell you a story…
John

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

No knowledge
in Android dev
ApiManager

John
MyActivity
ApiManager

John
MyActivity
MyActivity
ApiManager

John
MyApplication MyActivity
MyActivity
ApiManager DbManager

John
MyApplication MyActivity
MyActivity
Mary

New hire !
This codebase
looks so old! Let’s
add change a few
things…

Mary

New hire !
ApiManager DbManager

Mary

MyApplication MyActivity
MyActivity

John
Retrofit

ApiManager DbManager

Mary

MyApplication MyActivity
MyActivity

John
Retrofit

ApiManager DbManager

Mary
NewFeature
MyApplication MyActivity
MyActivity Activity

NewFeature
John
Fragment
Retrofit DbManager

ApiManager
RxJava

Mary
NewFeature
MyApplication MyActivity
MyActivity Activity

NewFeature
John
Fragment
App

John Mary
App

John Mary
No idea where this
crash is coming
from

App

John Mary
Francis

New hire !
OMG !
Zero test ?!

Francis

New hire !
Unit tests App

Francis

John Mary
Unit tests App

Francis
Hard to catch up
with the coverage &
too many Android dependencies

John Mary
App
UI tests
Francis

John Mary
App
UI tests
Francis
Interactions with outside
world = flaky tests

John Mary
App

John Mary Francis


1 year later…

App

John Mary Francis


App
Dependant
App
Dependant
App • UI and business logic
Dependant
App • UI and business logic
• Codebase and external libs
Dependant
App
Hard to test
Dependant
App
Hard to test
Hard to maintain
Another approach
Clean architecture

Systems should be
• Independent of Frameworks
• Testable
• Independent of UI
• Independent of Database
• Independent of any external agency
Let’s consider this problem
Data layer
Data layer

Domain layer
Data layer

Domain layer

Presentation layer
Data layer

Data layer

Domain layer
Data layer

Data layer

Domain layer
Data layer : repository pattern

Retrofit client Db client

ArtistRepository
Domain layer
Data layer

Domain layer

Presentation layer
Domain layer
Domain layer

• Orchestrates the flow of data with “use cases”


Domain layer

• Orchestrates the flow of data with “use cases”


• Offers its services to the presentation layer
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…)
Presentation layer

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

• Tied to the view lifecycle


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

• Tied to the view lifecycle


• Receives user inputs
<activity android:name=".SearchActivity"/>

• Tied to the view lifecycle


• Receives user inputs
• Associated to specific view layouts (ListActivity)
<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
Domain layer

Data

Presentation
View layer

Interaction
Domain layer

Data

Presentation layer View

Interaction
Domain layer

Data

Presentation layer
[ View
]
Interaction
Presentation layer

Data

Model Presenter View

Interaction
Presentation layer
interface SearchPresenter {

void searchArtist(String searchTerm);

void clickArtist(Artist artist);

}

interface SearchView {

void showProgress();

void hideProgress();

void showArtists(List<Artist> artists);

}

Model Presenter View


Model vs. View model

class Artist {

String displayName;

String uri;

String id;

LocalDate onTourUntil;

}
Model vs. View model

class Artist {

String displayName;

String uri;

String id;

LocalDate onTourUntil;

}
Model vs. View model

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;


class ArtistViewModel {

public ArtistViewHolder(View itemView) {

String name;
super(itemView);

boolean isOnTour;
ButterKnife.bind(this, itemView);

}
}


@Override

public void bind(ArtistViewModel viewModel) {

artistName.setText(artistViewModel.name);

onTour.setVisibility(artistViewModel.isOnTour ?
View.VISIBLE : View.GONE
);

}

}
Presentation layer
interface SearchPresenter {

void searchArtist(String searchTerm);

void clickArtist(ArtistViewModel artist);

}

interface SearchView {

void showProgress();

void hideProgress();

void showArtists(List<ArtistViewModel> artists);

}

View model Presenter View


Presentation layer
class SearchFragment extends Fragment implements SearchView {


SearchPresenter searchPresenter;


@Override

void showProgress() {

// ...

}


@Override

void hideProgress() {

// ...

}


@Override

void showArtists(List<ArtistViewModel> artists) {

// ...

}

{...}

}
Presentation layer

class SearchFragment extends Fragment implements SearchView {




SearchPresenter searchPresenter;


{...}

void onItemClick(ArtistViewModel artist) {

searchPresenter.clickArtist(artist);
}


void search(String searchTerm) {

searchPresenter.searchArtist(searchTerm);
}

}
Data layer

Domain layer

Presentation layer
Communication
Rx all the things!
Observable<Model> Data layer

Observable<ViewModel> Domain layer

Subscriber<ViewModel> Presentation layer


Communication
Rx all the things!

interface ArtistRepository {

Data layer Observable<List<Artist>> search(String searchTerm);

}
Communication
Rx all the things!

interface ArtistRepository {

Data layer Observable<List<Artist>> search(String searchTerm);

}

interface SearchArtistUseCase<VM extends ViewModel> {



void subscribe(Subscriber<VM> subscriber);
Domain layer
void unsubscribe();

}
Communication
Rx all the things!

interface ArtistRepository {

Data layer Observable<List<Artist>> search(String searchTerm);

}

interface SearchArtistUseCase<VM extends ViewModel> {



void subscribe(Subscriber<VM> subscriber);
Domain layer
void unsubscribe();

}

interface SearchPresenter {

void searchArtist(String searchTerm);
Presentation layer
void onDestroy();

}
Structure
Dagger
Structure
Dagger

Application

component
Structure Application
modules
Repositories

Dagger

Application

component
Structure Application
modules
Repositories

Dagger

Activity
component

Application

component
Structure Application
modules
Repositories

Dagger
Activity
modules
Activity

Activity
component

Application

component
Structure Application
modules
Repositories

Dagger
Activity
modules
Activity

Fragment
modules
Presenter
Use cases
Activity
component

Application

component
Structure
Structure

“Program to an interface, not an implementation”


— Erich Gamma
Structure

“Program to an interface, not an implementation”


— Erich Gamma

• Decouple the client from the implementation


Structure

“Program to an interface, not an implementation”


— Erich Gamma

• Decouple the client from the implementation


• Defines the vocabulary of the collaboration
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
Testing
Unit
Data layer ArtistRepository

Domain layer SearchUseCase

SearchPresenter
Presentation layer
SearchFragment
Testing
Unit
Data layer ArtistRepository

Domain layer SearchUseCase

SearchPresenter
Presentation layer
Testable without
SearchFragment
Robolectric
Testing
Unit
Data layer ArtistRepository

Domain layer SearchUseCase

Testable with
Robolectric
SearchPresenter
Presentation layer
Testable without
SearchFragment
Robolectric
Testing
UI
Data layer

Domain layer

Presentation layer
Testing .json

UI
Data layer

Domain layer

Presentation layer
Testing .json
UI class ArtistRepositoryTestImpl {


JsonLoader jsonLoader;


Data layer Observable<List<Artist>> search(String searchTerm) {

return jsonLoader.load(“search/artist.json”);

}

}

Domain layer

Presentation layer
Conclusion
Conclusion

• Choose an architecture and stick with it


Conclusion

• Choose an architecture and stick with it


• Test while you code
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
Songkick

We’re hiring!
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
Thank you

You might also like