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

Clean architecture on Android

Clean architecture on Android

Overview and implementation examples (code snippets) for clean architecture implementation on Android.
Talk @ Droidcon Zagreb 2016

Marko Milos

April 28, 2016
Tweet

Other Decks in Programming

Transcript

  1. Clean architecture • Independent of frameworks • Testable • Independent

    of UI • Independent of database / storage Undabot
  2. Clean architecture • Independent of frameworks • Testable • Independent

    of UI • Independent of database / storage • Independent of any external agency Undabot
  3. Layers Entities UseCase Abstraction Presenter Repository UI Data source UseCase

    / Interactors Entities Abstraction DOMAIN Repository Data source DATA
  4. Layers Entities UseCase Abstraction Presenter Repository UI Data source UI

    Presenter PRESENTATION UseCase / Interactors Entities Abstraction DOMAIN Repository Data source DATA
  5. Presentation Model View Presenter (MVP) View • bind data to

    view • scheduling animations / transitions • propagate user input to presenter • Activity, Fragment, View Undabot
  6. Presentation • orchestrate and execute use cases • prepare /

    format data for the view Model View Presenter (MVP) View • bind data to view • scheduling animations / transitions • propagate user input to presenter • Activity, Fragment, View Presenter Undabot
  7. Flow View Presenter D O M A I N bind(v)

    execute(s) data Undabot
  8. Flow View Presenter D O M A I N bind(v)

    execute(s) data process
 format Undabot
  9. Flow View Presenter D O M A I N bind(v)

    execute(s) data process
 format display(d) Undabot
  10. Flow View Presenter D O M A I N execute(s)

    data process
 format display(d) [user input] onInput() Undabot
  11. Abstraction public class Timeline {
 List<Tweet> tweets;
 } public interface

    TimelineView extends View {
 
 void displayError(String message);
 
 void displayTimeline(List<Tweet> tweets);
 
 } public class Timeline {
 List<Tweet> tweets;
 } Undabot
  12. Abstraction public class Timeline {
 List<Tweet> tweets;
 } public interface

    TimelineView extends View {
 
 void displayError(String message);
 
 void displayTimeline(List<Tweet> tweets);
 
 } public interface TimelineView extends View {
 
 void displayError(String message);
 
 void displayTimeline(List<Tweet> tweets);
 
 } public class Timeline {
 List<Tweet> tweets;
 } Undabot
  13. Abstraction public class Timeline {
 List<Tweet> tweets;
 } public interface

    TimelineView extends View {
 
 void displayError(String message);
 
 void displayTimeline(List<Tweet> tweets);
 
 } public interface TimelineView extends View {
 
 void displayError(String message);
 
 void displayTimeline(List<Tweet> tweets);
 
 } public class Timeline {
 List<Tweet> tweets;
 } public interface TimelineView extends View {
 
 void displayError(String message);
 
 void displayTimeline(List<Tweet> tweets);
 
 } Undabot
  14. Abstraction public class Timeline {
 List<Tweet> tweets;
 } public interface

    TimelineView extends View {
 
 void displayError(String message);
 
 void displayTimeline(List<Tweet> tweets);
 
 } public interface TimelineView extends View {
 
 void displayError(String message);
 
 void displayTimeline(List<Tweet> tweets);
 
 } public interface TimelinePresenter extends Presenter<TimelineView> { 
 void onRefresh();
 
 void onTweetSelected(Tweet tweet); 
 } public class Timeline {
 List<Tweet> tweets;
 } public interface TimelineView extends View {
 
 void displayError(String message);
 
 void displayTimeline(List<Tweet> tweets);
 
 } public interface TimelineView extends View {
 
 void displayError(String message);
 
 void displayTimeline(List<Tweet> tweets);
 
 } Undabot
  15. Abstraction public class Timeline {
 List<Tweet> tweets;
 } public interface

    TimelineView extends View {
 
 void displayError(String message);
 
 void displayTimeline(List<Tweet> tweets);
 
 } public interface TimelineView extends View {
 
 void displayError(String message);
 
 void displayTimeline(List<Tweet> tweets);
 
 } public interface TimelinePresenter extends Presenter<TimelineView> { 
 void onRefresh();
 
 void onTweetSelected(Tweet tweet); 
 } public interface TimelinePresenter extends Presenter<TimelineView> { 
 void onRefresh();
 
 void onTweetSelected(Tweet tweet); 
 } public class Timeline {
 List<Tweet> tweets;
 } public interface TimelineView extends View {
 
 void displayError(String message);
 
 void displayTimeline(List<Tweet> tweets);
 
 } public interface TimelineView extends View {
 
 void displayError(String message);
 
 void displayTimeline(List<Tweet> tweets);
 
 } Undabot
  16. Abstraction public class Timeline {
 List<Tweet> tweets;
 } public interface

    TimelineView extends View {
 
 void displayError(String message);
 
 void displayTimeline(List<Tweet> tweets);
 
 } public interface TimelineView extends View {
 
 void displayError(String message);
 
 void displayTimeline(List<Tweet> tweets);
 
 } public interface TimelinePresenter extends Presenter<TimelineView> { 
 void onRefresh();
 
 void onTweetSelected(Tweet tweet); 
 } public interface TimelinePresenter extends Presenter<TimelineView> { 
 void onRefresh();
 
 void onTweetSelected(Tweet tweet); 
 } public interface TimelinePresenter extends Presenter<TimelineView> { 
 void onRefresh();
 
 void onTweetSelected(Tweet tweet); 
 } public class Timeline {
 List<Tweet> tweets;
 } public interface TimelineView extends View {
 
 void displayError(String message);
 
 void displayTimeline(List<Tweet> tweets);
 
 } public interface TimelineView extends View {
 
 void displayError(String message);
 
 void displayTimeline(List<Tweet> tweets);
 
 } Undabot
  17. View implementation public class TimelineActivity extends AppCompatActivity implements TimelineView {


    
 @Inject TimelinePresenter timelinePresenter;
 
 SwipeRefreshLayout swipeRefreshLayout;
 
 @Override
 protected void onCreate(@Nullable Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 
 injectDependencies();
 timelinePresenter.bind(this);
 
 swipeRefreshLayout.setOnRefreshListener(() -> timelinePresenter.onRefresh());
 }
 
 @Override
 public void displayError(String message) {
 /* display error */
 }
 
 @Override
 public void displayTimeline(Timeline timeline) {
 /* setup recycler, adapter etc */
 }
 } Undabot
  18. View implementation public class TimelineActivity extends AppCompatActivity implements TimelineView {


    
 @Inject TimelinePresenter timelinePresenter;
 
 SwipeRefreshLayout swipeRefreshLayout;
 
 @Override
 protected void onCreate(@Nullable Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 
 injectDependencies();
 timelinePresenter.bind(this);
 
 swipeRefreshLayout.setOnRefreshListener(() -> timelinePresenter.onRefresh());
 }
 
 @Override
 public void displayError(String message) {
 /* display error */
 }
 
 @Override
 public void displayTimeline(Timeline timeline) {
 /* setup recycler, adapter etc */
 }
 } Undabot
  19. View implementation public class TimelineActivity extends AppCompatActivity implements TimelineView {


    
 @Inject TimelinePresenter timelinePresenter;
 
 SwipeRefreshLayout swipeRefreshLayout;
 
 @Override
 protected void onCreate(@Nullable Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 
 injectDependencies();
 timelinePresenter.bind(this);
 
 swipeRefreshLayout.setOnRefreshListener(() -> timelinePresenter.onRefresh());
 }
 
 @Override
 public void displayError(String message) {
 /* display error */
 }
 
 @Override
 public void displayTimeline(Timeline timeline) {
 /* setup recycler, adapter etc */
 }
 } public class TimelineActivity extends AppCompatActivity implements TimelineView {
 
 @Inject TimelinePresenter timelinePresenter;
 
 SwipeRefreshLayout swipeRefreshLayout;
 
 @Override
 protected void onCreate(@Nullable Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 
 injectDependencies();
 timelinePresenter.bind(this);
 
 swipeRefreshLayout.setOnRefreshListener(() -> timelinePresenter.onRefresh());
 }
 
 @Override
 public void displayError(String message) {
 /* display error */
 }
 
 @Override
 public void displayTimeline(Timeline timeline) {
 /* setup recycler, adapter etc */
 }
 } Undabot
  20. View implementation public class TimelineActivity extends AppCompatActivity implements TimelineView {


    
 @Inject TimelinePresenter timelinePresenter;
 
 SwipeRefreshLayout swipeRefreshLayout;
 
 @Override
 protected void onCreate(@Nullable Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 
 injectDependencies();
 timelinePresenter.bind(this);
 
 swipeRefreshLayout.setOnRefreshListener(() -> timelinePresenter.onRefresh());
 }
 
 @Override
 public void displayError(String message) {
 /* display error */
 }
 
 @Override
 public void displayTimeline(Timeline timeline) {
 /* setup recycler, adapter etc */
 }
 } public class TimelineActivity extends AppCompatActivity implements TimelineView {
 
 @Inject TimelinePresenter timelinePresenter;
 
 SwipeRefreshLayout swipeRefreshLayout;
 
 @Override
 protected void onCreate(@Nullable Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 
 injectDependencies();
 timelinePresenter.bind(this);
 
 swipeRefreshLayout.setOnRefreshListener(() -> timelinePresenter.onRefresh());
 }
 
 @Override
 public void displayError(String message) {
 /* display error */
 }
 
 @Override
 public void displayTimeline(Timeline timeline) {
 /* setup recycler, adapter etc */
 }
 } public class TimelineActivity extends AppCompatActivity implements TimelineView {
 
 @Inject TimelinePresenter timelinePresenter;
 
 SwipeRefreshLayout swipeRefreshLayout;
 
 @Override
 protected void onCreate(@Nullable Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 
 injectDependencies();
 timelinePresenter.bind(this);
 
 swipeRefreshLayout.setOnRefreshListener(() -> timelinePresenter.onRefresh());
 }
 
 @Override
 public void displayError(String message) {
 /* display error */
 }
 
 @Override
 public void displayTimeline(Timeline timeline) {
 /* setup recycler, adapter etc */
 }
 } Undabot
  21. public class TimelinePresenterImpl extends AbsPresenter<TimelineView> implements TimelinePresenter {
 
 private

    final Navigator navigator;
 private final GetTimelineUseCase useCase;
 
 @Inject
 public TimelinePresenterImpl(Navigator navigator, GetTimelineUseCase useCase) {
 this.navigator = navigator;
 this.useCase = useCase;
 }
 
 @Override
 protected void onBind() {
 useCase.execute(new TimelineSubscriber());
 }
 
 @Override
 public void onRefresh() {
 useCase.execute(new TimelineSubscriber());
 }
 
 @Override
 public void onTweetSelected(Tweet tweet) {
 navigator.openTweetDetails(tweet);
 }
 } Presenter implementation Undabot
  22. Presenter implementation public class TimelinePresenterImpl extends AbsPresenter<TimelineView> implements TimelinePresenter {


    
 private final Navigator navigator;
 private final GetTimelineUseCase useCase;
 
 @Inject
 public TimelinePresenterImpl(Navigator navigator, GetTimelineUseCase useCase) {
 this.navigator = navigator;
 this.useCase = useCase;
 }
 
 @Override
 protected void onBind() {
 useCase.execute(new TimelineSubscriber());
 }
 
 @Override
 public void onRefresh() {
 useCase.execute(new TimelineSubscriber());
 }
 
 @Override
 public void onTweetSelected(Tweet tweet) {
 navigator.openTweetDetails(tweet);
 }
 } Undabot
  23. Presenter implementation public class TimelinePresenterImpl extends AbsPresenter<TimelineView> implements TimelinePresenter {


    
 private final Navigator navigator;
 private final GetTimelineUseCase useCase;
 
 @Inject
 public TimelinePresenterImpl(Navigator navigator, GetTimelineUseCase useCase) {
 this.navigator = navigator;
 this.useCase = useCase;
 }
 
 @Override
 protected void onBind() {
 useCase.execute(new TimelineSubscriber());
 }
 
 @Override
 public void onRefresh() {
 useCase.execute(new TimelineSubscriber());
 }
 
 @Override
 public void onTweetSelected(Tweet tweet) {
 navigator.openTweetDetails(tweet);
 }
 } public class TimelinePresenterImpl extends AbsPresenter<TimelineView> implements TimelinePresenter {
 
 private final Navigator navigator;
 private final GetTimelineUseCase useCase;
 
 @Inject
 public TimelinePresenterImpl(Navigator navigator, GetTimelineUseCase useCase) {
 this.navigator = navigator;
 this.useCase = useCase;
 }
 
 @Override
 protected void onBind() {
 useCase.execute(new TimelineSubscriber());
 }
 
 @Override
 public void onRefresh() {
 useCase.execute(new TimelineSubscriber());
 }
 
 @Override
 public void onTweetSelected(Tweet tweet) {
 navigator.openTweetDetails(tweet);
 }
 } Undabot
  24. Presenter implementation public class TimelinePresenterImpl extends AbsPresenter<TimelineView> implements TimelinePresenter {


    
 private final Navigator navigator;
 private final GetTimelineUseCase useCase;
 
 @Inject
 public TimelinePresenterImpl(Navigator navigator, GetTimelineUseCase useCase) {
 this.navigator = navigator;
 this.useCase = useCase;
 }
 
 @Override
 protected void onBind() {
 useCase.execute(new TimelineSubscriber());
 }
 
 @Override
 public void onRefresh() {
 useCase.execute(new TimelineSubscriber());
 }
 
 @Override
 public void onTweetSelected(Tweet tweet) {
 navigator.openTweetDetails(tweet);
 }
 } public class TimelinePresenterImpl extends AbsPresenter<TimelineView> implements TimelinePresenter {
 
 private final Navigator navigator;
 private final GetTimelineUseCase useCase;
 
 @Inject
 public TimelinePresenterImpl(Navigator navigator, GetTimelineUseCase useCase) {
 this.navigator = navigator;
 this.useCase = useCase;
 }
 
 @Override
 protected void onBind() {
 useCase.execute(new TimelineSubscriber());
 }
 
 @Override
 public void onRefresh() {
 useCase.execute(new TimelineSubscriber());
 }
 
 @Override
 public void onTweetSelected(Tweet tweet) {
 navigator.openTweetDetails(tweet);
 }
 } public class TimelinePresenterImpl extends AbsPresenter<TimelineView> implements TimelinePresenter {
 
 private final Navigator navigator;
 private final GetTimelineUseCase useCase;
 
 @Inject
 public TimelinePresenterImpl(Navigator navigator, GetTimelineUseCase useCase) {
 this.navigator = navigator;
 this.useCase = useCase;
 }
 
 @Override
 protected void onBind() {
 useCase.execute(new TimelineSubscriber());
 }
 
 @Override
 public void onRefresh() {
 useCase.execute(new TimelineSubscriber());
 }
 
 @Override
 public void onTweetSelected(Tweet tweet) {
 navigator.openTweetDetails(tweet);
 }
 } Undabot
  25. Presenter implementation public class TimelinePresenterImpl extends AbsPresenter<TimelineView> implements TimelinePresenter {


    
 private final Navigator navigator;
 private final GetTimelineUseCase useCase;
 
 @Inject
 public TimelinePresenterImpl(Navigator navigator, GetTimelineUseCase useCase) {
 this.navigator = navigator;
 this.useCase = useCase;
 }
 
 @Override
 protected void onBind() {
 useCase.execute(new TimelineSubscriber());
 }
 
 @Override
 public void onRefresh() {
 useCase.execute(new TimelineSubscriber());
 }
 
 @Override
 public void onTweetSelected(Tweet tweet) {
 navigator.openTweetDetails(tweet);
 }
 } public class TimelinePresenterImpl extends AbsPresenter<TimelineView> implements TimelinePresenter {
 
 private final Navigator navigator;
 private final GetTimelineUseCase useCase;
 
 @Inject
 public TimelinePresenterImpl(Navigator navigator, GetTimelineUseCase useCase) {
 this.navigator = navigator;
 this.useCase = useCase;
 }
 
 @Override
 protected void onBind() {
 useCase.execute(new TimelineSubscriber());
 }
 
 @Override
 public void onRefresh() {
 useCase.execute(new TimelineSubscriber());
 }
 
 @Override
 public void onTweetSelected(Tweet tweet) {
 navigator.openTweetDetails(tweet);
 }
 } public class TimelinePresenterImpl extends AbsPresenter<TimelineView> implements TimelinePresenter {
 
 private final Navigator navigator;
 private final GetTimelineUseCase useCase;
 
 @Inject
 public TimelinePresenterImpl(Navigator navigator, GetTimelineUseCase useCase) {
 this.navigator = navigator;
 this.useCase = useCase;
 }
 
 @Override
 protected void onBind() {
 useCase.execute(new TimelineSubscriber());
 }
 
 @Override
 public void onRefresh() {
 useCase.execute(new TimelineSubscriber());
 }
 
 @Override
 public void onTweetSelected(Tweet tweet) {
 navigator.openTweetDetails(tweet);
 }
 } public class TimelinePresenterImpl extends AbsPresenter<TimelineView> implements TimelinePresenter {
 
 private final Navigator navigator;
 private final GetTimelineUseCase useCase;
 
 @Inject
 public TimelinePresenterImpl(Navigator navigator, GetTimelineUseCase useCase) {
 this.navigator = navigator;
 this.useCase = useCase;
 }
 
 @Override
 protected void onBind() {
 useCase.execute(new TimelineSubscriber());
 }
 
 @Override
 public void onRefresh() {
 useCase.execute(new TimelineSubscriber());
 }
 
 @Override
 public void onTweetSelected(Tweet tweet) {
 navigator.openTweetDetails(tweet);
 }
 } Undabot
  26. Callback / Subscriber private final class TimelineSubscriber extends Subscriber<Timeline> {


    @Override
 public void onCompleted() {
 }
 
 @Override
 public void onError(Throwable e) {
 view().displayError(ErrorMessageFactory.create(e));
 }
 
 @Override
 public void onNext(Timeline timeline) {
 view().displayTimeline(timeline.tweets);
 }
 } Undabot
  27. Callback / Subscriber private final class TimelineSubscriber extends Subscriber<Timeline> {


    @Override
 public void onCompleted() {
 }
 
 @Override
 public void onError(Throwable e) {
 view().displayError(ErrorMessageFactory.create(e));
 }
 
 @Override
 public void onNext(Timeline timeline) {
 view().displayTimeline(timeline.tweets);
 }
 } Undabot
  28. Callback / Subscriber private final class TimelineSubscriber extends Subscriber<Timeline> {


    @Override
 public void onCompleted() {
 }
 
 @Override
 public void onError(Throwable e) {
 view().displayError(ErrorMessageFactory.create(e));
 }
 
 @Override
 public void onNext(Timeline timeline) {
 view().displayTimeline(timeline.tweets);
 }
 } private final class TimelineSubscriber extends Subscriber<Timeline> {
 @Override
 public void onCompleted() {
 }
 
 @Override
 public void onError(Throwable e) {
 view().displayError(ErrorMessageFactory.create(e));
 }
 
 @Override
 public void onNext(Timeline timeline) {
 view().displayTimeline(timeline.tweets);
 }
 } Undabot
  29. P R E S E N T A T I

    O N Domain D A T A Undabot
  30. P R E S E N T A T I

    O N Domain D A T A UseCase execute(s) Undabot
  31. P R E S E N T A T I

    O N Domain D A T A UseCase Model execute(s) Undabot
  32. P R E S E N T A T I

    O N Domain D A T A UseCase Abstraction Abstraction Abstraction Model execute(s) Undabot
  33. P R E S E N T A T I

    O N Domain D A T A UseCase Abstraction Abstraction Abstraction Model execute(s) Undabot
  34. P R E S E N T A T I

    O N Domain D A T A UseCase Abstraction Abstraction Abstraction Model Business logic execute(s) Undabot
  35. P R E S E N T A T I

    O N Domain D A T A UseCase Abstraction Abstraction Abstraction Model Business logic execute(s) s.onNext(d)
 s.onError(t) Undabot
  36. Model UseCase Abstraction Abstraction Abstraction Model Business logic public class

    Tweet {
 private String author;
 private String content;
 
 public Tweet(String author, String content) {
 this.author = author;
 this.content = content;
 }
 } public class Timeline {
 private List<Tweet> tweets;
 
 public Timeline(List<Tweet> tweets) {
 this.tweets = tweets;
 }
 } public class Location {
 private float lat;
 private float lng;
 
 public Location(float lat, float lng) {
 this.lat = lat;
 this.lng = lng;
 }
 } public class Region {
 private Bounds bounds;
 private Location location;
 private Map<Integer, Area> areas;
 
 public Region(Bounds bounds, Location location) {
 this.bounds = bounds;
 this.location = location;
 }
 } Undabot
  37. public interface Interactor<T> {
 void execute(Callback<T> callback);
 } Interactor UseCase

    Abstraction Abstraction Abstraction Model Business logic Undabot
  38. Interactor UseCase Abstraction Abstraction Abstraction Model Business logic public interface

    Interactor<T> {
 void execute(Subscriber<T> subscriber);
 } Undabot
  39. Interactor UseCase Abstraction Abstraction Abstraction Model Business logic public interface

    Interactor<T> {
 void execute(Subscriber<T> subscriber);
 
 Observable<T> execute();
 } Undabot
  40. Contract UseCase Abstraction Abstraction Abstraction Model Business logic abstract class

    UseCase<T> implements Interactor<T> {
 
 private final ThreadExecutor threadExecutor;
 private final PostExecutionThread postExecutionThread;
 
 private Subscription subscription = Subscriptions.empty();
 
 public UseCase(ThreadExecutor threadExecutor, PostExecutionThread postExecutionThread) {
 this.threadExecutor = threadExecutor;
 this.postExecutionThread = postExecutionThread;
 }
 
 abstract Observable<T> createObservable();
 
 @Override
 public void execute(Subscriber<T> subscriber) {
 this.subscription = createObservable()
 .subscribeOn(Schedulers.from(threadExecutor))
 .observeOn(postExecutionThread.getScheduler())
 .subscribe(subscriber);
 }
 
 @Override
 public Observable<T> execute() {
 return createObservable();
 }
 
 public void unsubscribe() {
 if (!subscription.isUnsubscribed()) {
 subscription.unsubscribe();
 }
 }
 } Undabot
  41. Contract UseCase Abstraction Abstraction Abstraction Model Business logic abstract class

    UseCase<T> implements Interactor<T> {
 
 private final ThreadExecutor threadExecutor;
 private final PostExecutionThread postExecutionThread;
 
 private Subscription subscription = Subscriptions.empty();
 
 public UseCase(ThreadExecutor threadExecutor, PostExecutionThread postExecutionThread) {
 this.threadExecutor = threadExecutor;
 this.postExecutionThread = postExecutionThread;
 }
 
 abstract Observable<T> createObservable();
 
 @Override
 public void execute(Subscriber<T> subscriber) {
 this.subscription = createObservable()
 .subscribeOn(Schedulers.from(threadExecutor))
 .observeOn(postExecutionThread.getScheduler())
 .subscribe(subscriber);
 }
 
 @Override
 public Observable<T> execute() {
 return createObservable();
 }
 
 public void unsubscribe() {
 if (!subscription.isUnsubscribed()) {
 subscription.unsubscribe();
 }
 }
 } Undabot
  42. Contract UseCase Abstraction Abstraction Abstraction Model Business logic abstract class

    UseCase<T> implements Interactor<T> {
 
 private final ThreadExecutor threadExecutor;
 private final PostExecutionThread postExecutionThread;
 
 private Subscription subscription = Subscriptions.empty();
 
 public UseCase(ThreadExecutor threadExecutor, PostExecutionThread postExecutionThread) {
 this.threadExecutor = threadExecutor;
 this.postExecutionThread = postExecutionThread;
 }
 
 abstract Observable<T> createObservable();
 
 @Override
 public void execute(Subscriber<T> subscriber) {
 this.subscription = createObservable()
 .subscribeOn(Schedulers.from(threadExecutor))
 .observeOn(postExecutionThread.getScheduler())
 .subscribe(subscriber);
 }
 
 @Override
 public Observable<T> execute() {
 return createObservable();
 }
 
 public void unsubscribe() {
 if (!subscription.isUnsubscribed()) {
 subscription.unsubscribe();
 }
 }
 } Undabot
  43. Contract UseCase Abstraction Abstraction Abstraction Model Business logic abstract class

    UseCase<T> implements Interactor<T> {
 
 private final ThreadExecutor threadExecutor;
 private final PostExecutionThread postExecutionThread;
 
 private Subscription subscription = Subscriptions.empty();
 
 public UseCase(ThreadExecutor threadExecutor, PostExecutionThread postExecutionThread) {
 this.threadExecutor = threadExecutor;
 this.postExecutionThread = postExecutionThread;
 }
 
 abstract Observable<T> createObservable();
 
 @Override
 public void execute(Subscriber<T> subscriber) {
 this.subscription = createObservable()
 .subscribeOn(Schedulers.from(threadExecutor))
 .observeOn(postExecutionThread.getScheduler())
 .subscribe(subscriber);
 }
 
 @Override
 public Observable<T> execute() {
 return createObservable();
 }
 
 public void unsubscribe() {
 if (!subscription.isUnsubscribed()) {
 subscription.unsubscribe();
 }
 }
 } Undabot
  44. Implementation UseCase Abstraction Abstraction Abstraction Model Business logic public class

    GetTimelineUseCase extends UseCase<Timeline> {
 
 private final TimelineRepository timelineRepository;
 
 @Inject
 public GetTimelineUseCase(ThreadExecutor threadExecutor,
 PostExecutionThread postExecutionThread,
 TimelineRepository timelineRepository) {
 super(threadExecutor, postExecutionThread);
 this.timelineRepository = timelineRepository;
 }
 
 @Override
 Observable<Timeline> createObservable() {
 return timelineRepository.getTimeline();
 }
 } Undabot
  45. Implementation UseCase Abstraction Abstraction Abstraction Model Business logic public class

    GetTimelineUseCase extends UseCase<Timeline> {
 
 private final TimelineRepository timelineRepository;
 
 @Inject
 public GetTimelineUseCase(ThreadExecutor threadExecutor,
 PostExecutionThread postExecutionThread,
 TimelineRepository timelineRepository) {
 super(threadExecutor, postExecutionThread);
 this.timelineRepository = timelineRepository;
 }
 
 @Override
 Observable<Timeline> createObservable() {
 return timelineRepository.getTimeline();
 }
 } Undabot
  46. Implementation UseCase Abstraction Abstraction Abstraction Model Business logic public class

    GetTimelineUseCase extends UseCase<Timeline> {
 
 private final TimelineRepository timelineRepository;
 
 @Inject
 public GetTimelineUseCase(ThreadExecutor threadExecutor,
 PostExecutionThread postExecutionThread,
 TimelineRepository timelineRepository) {
 super(threadExecutor, postExecutionThread);
 this.timelineRepository = timelineRepository;
 }
 
 @Override
 Observable<Timeline> createObservable() {
 return timelineRepository.getTimeline();
 }
 } Undabot
  47. Abstraction UseCase Abstraction Abstraction Abstraction Model Business logic public interface

    TimelineRepository {
 
 Observable<Timeline> getTimeline();
 
 Observable<Timeline> getTimeline(String hashTag);
 
 Observable<Timeline> getTimeline(Region region);
 
 } Undabot
  48. Business logic UseCase Abstraction Abstraction Abstraction Model Business logic public

    final class LocationOperations {
 
 /* Initialization */
 
 public Region toRegion(Location location) {
 /* Business logic implementation */ 
 }
 } Undabot
  49. Regional timeline feature get timeline for region calculate region get

    current location location available ? get default location no yes
  50. Regional timeline feature get timeline for region calculate region get

    current location location available ? get default location no yes GetLocationUseCase
  51. Regional timeline feature get timeline for region calculate region get

    current location location available ? get default location no yes GetLocationUseCase Business 
 logic
  52. Regional timeline feature get timeline for region calculate region get

    current location location available ? get default location no yes GetLocationUseCase Business 
 logic GetRegionalTimelineUseCase
  53. Location get current location location available ? get default location

    no yes public class GetLocationUseCase extends UseCase<Location> {
 
 private final LocationProvider locationProvider;
 
 @Inject
 public GetLocationUseCase(ThreadExecutor threadExecutor,
 PostExecutionThread postExecutionThread,
 LocationProvider locationProvider) {
 super(threadExecutor, postExecutionThread);
 this.locationProvider = locationProvider;
 }
 
 @Override
 Observable<Location> createObservable() {
 return Observable
 .concat(locationProvider.getCurrentLocation(), locationProvider.getDefaultLocation())
 .first(location -> location != null);
 }
 } Undabot
  54. Location get current location location available ? get default location

    no yes public class GetLocationUseCase extends UseCase<Location> {
 
 private final LocationProvider locationProvider;
 
 @Inject
 public GetLocationUseCase(ThreadExecutor threadExecutor,
 PostExecutionThread postExecutionThread,
 LocationProvider locationProvider) {
 super(threadExecutor, postExecutionThread);
 this.locationProvider = locationProvider;
 }
 
 @Override
 Observable<Location> createObservable() {
 return Observable
 .concat(locationProvider.getCurrentLocation(), locationProvider.getDefaultLocation())
 .first(location -> location != null);
 }
 } Undabot
  55. Location get current location location available ? get default location

    no yes public class GetLocationUseCase extends UseCase<Location> {
 
 private final LocationProvider locationProvider;
 
 @Inject
 public GetLocationUseCase(ThreadExecutor threadExecutor,
 PostExecutionThread postExecutionThread,
 LocationProvider locationProvider) {
 super(threadExecutor, postExecutionThread);
 this.locationProvider = locationProvider;
 }
 
 @Override
 Observable<Location> createObservable() {
 return Observable
 .concat(locationProvider.getCurrentLocation(), locationProvider.getDefaultLocation())
 .first(location -> location != null);
 }
 } Undabot
  56. Location get current location location available ? get default location

    no yes public class GetLocationUseCase extends UseCase<Location> {
 
 private final LocationProvider locationProvider;
 
 @Inject
 public GetLocationUseCase(ThreadExecutor threadExecutor,
 PostExecutionThread postExecutionThread,
 LocationProvider locationProvider) {
 super(threadExecutor, postExecutionThread);
 this.locationProvider = locationProvider;
 }
 
 @Override
 Observable<Location> createObservable() {
 return Observable
 .concat(locationProvider.getCurrentLocation(), locationProvider.getDefaultLocation())
 .first(location -> location != null);
 }
 } public interface LocationProvider {
 
 Observable<Location> getCurrentLocation();
 
 Observable<Location> getDefaultLocation();
 
 } Undabot
  57. Regional timeline feature get timeline for region calculate region get

    current location location available ? get default location no yes GetLocationUseCase Business 
 logic GetRegionalTimelineUseCase
  58. Regional timeline use case public class GetRegionalTimelineUseCase extends UseCase<Timeline> {


    
 private final GetLocationUseCase locationUseCase;
 private final LocationOperations locationOperations;
 private final TimelineRepository timelineRepository;
 
 @Inject
 public GetRegionalTimelineUseCase(ThreadExecutor threadExecutor,
 PostExecutionThread postExecutionThread,
 GetLocationUseCase locationUseCase,
 LocationOperations locationOperations,
 TimelineRepository timelineRepository) {
 super(threadExecutor, postExecutionThread);
 this.locationUseCase = locationUseCase;
 this.locationOperations = locationOperations;
 this.timelineRepository = timelineRepository;
 }
 
 
 } GetRegionalTimelineUseCase Undabot
  59. Regional timeline use case @Override
 Observable<Timeline> createObservable() {
 return locationUseCase


    .execute()
 .map(new Func1<Location, Region>() {
 @Override
 public Region call(Location location) {
 return locationOperations.toRegion(location);
 }
 })
 .flatMap(new Func1<Region, Observable<Timeline>>() {
 @Override
 public Observable<Timeline> call(Region region) {
 return timelineRepository.getTimeline(region);
 }
 });
 } public class GetRegionalTimelineUseCase extends UseCase<Timeline> {
 
 private final GetLocationUseCase locationUseCase;
 private final LocationOperations locationOperations;
 private final TimelineRepository timelineRepository;
 
 @Inject
 public GetRegionalTimelineUseCase(ThreadExecutor threadExecutor,
 PostExecutionThread postExecutionThread,
 GetLocationUseCase locationUseCase,
 LocationOperations locationOperations,
 TimelineRepository timelineRepository) {
 super(threadExecutor, postExecutionThread);
 this.locationUseCase = locationUseCase;
 this.locationOperations = locationOperations;
 this.timelineRepository = timelineRepository;
 }
 
 
 } GetRegionalTimelineUseCase Undabot
  60. Regional timeline use case @Override
 Observable<Timeline> createObservable() {
 return locationUseCase


    .execute()
 .map(location -> locationOperations.toRegion(location))
 .flatMap(region -> timelineRepository.getTimeline(region));
 } public class GetRegionalTimelineUseCase extends UseCase<Timeline> {
 
 private final GetLocationUseCase locationUseCase;
 private final LocationOperations locationOperations;
 private final TimelineRepository timelineRepository;
 
 @Inject
 public GetRegionalTimelineUseCase(ThreadExecutor threadExecutor,
 PostExecutionThread postExecutionThread,
 GetLocationUseCase locationUseCase,
 LocationOperations locationOperations,
 TimelineRepository timelineRepository) {
 super(threadExecutor, postExecutionThread);
 this.locationUseCase = locationUseCase;
 this.locationOperations = locationOperations;
 this.timelineRepository = timelineRepository;
 }
 
 
 } GetRegionalTimelineUseCase Undabot
  61. Regional timeline use case @Override
 Observable<Timeline> createObservable() {
 return locationUseCase


    .execute()
 .map(locationOperations::toRegion)
 .flatMap(timelineRepository::getTimeline);
 } public class GetRegionalTimelineUseCase extends UseCase<Timeline> {
 
 private final GetLocationUseCase locationUseCase;
 private final LocationOperations locationOperations;
 private final TimelineRepository timelineRepository;
 
 @Inject
 public GetRegionalTimelineUseCase(ThreadExecutor threadExecutor,
 PostExecutionThread postExecutionThread,
 GetLocationUseCase locationUseCase,
 LocationOperations locationOperations,
 TimelineRepository timelineRepository) {
 super(threadExecutor, postExecutionThread);
 this.locationUseCase = locationUseCase;
 this.locationOperations = locationOperations;
 this.timelineRepository = timelineRepository;
 }
 
 
 } GetRegionalTimelineUseCase Undabot
  62. Data P R E S E N T A T

    I O N D O M A I N Undabot
  63. Data P R E S E N T A T

    I O N D O M A I N Repository Undabot
  64. Data P R E S E N T A T

    I O N D O M A I N Repository Data source Undabot
  65. Data P R E S E N T A T

    I O N D O M A I N Repository Data source Network Disk DB Memory Undabot
  66. Data P R E S E N T A T

    I O N D O M A I N Cache ORM Cache Retrofit Repository Data source Network Disk DB Memory Undabot
  67. Data P R E S E N T A T

    I O N D O M A I N Cache ORM Cache Retrofit Repository Data source Network Disk DB Memory Data source factory Undabot
  68. Data P R E S E N T A T

    I O N D O M A I N Cache ORM Cache Retrofit Repository Data source Network Disk DB Memory Undabot
  69. Data source public interface TimelineDataSource {
 
 Observable<Timeline> getTimeline();
 


    Observable<Timeline> getTimeline(String hashTag);
 
 Observable<Timeline> getTimeline(Region region);
 
 // Other
 } Repository Data source Network Disk DB Memory Undabot
  70. Network implementation public class TimeLineDataSourceNetwork implements TimeLineDataSource {
 
 private

    final TwitterAPI twitterAPI;
 private final TimelineResponseMapper mapper;
 
 public TimeLineDataSourceNetwork(TwitterAPI twitterAPI, TimelineResponseMapper mapper) {
 this.twitterAPI = twitterAPI;
 this.mapper = mapper;
 }
 
 @Override
 public Observable<Timeline> getTimeline() {
 return twitterAPI
 .getTimeline()
 .retryWhen(new RetryWithDelay(3, 500))
 .map(timelineResponseResult -> mapper.toTimeline(timelineResponseResult.response()));
 }
 
 /* Other methods/implementations */
 } Repository Data source Network Disk DB Memory Undabot
  71. Network implementation public class TimeLineDataSourceNetwork implements TimeLineDataSource {
 
 private

    final TwitterAPI twitterAPI;
 private final TimelineResponseMapper mapper;
 
 public TimeLineDataSourceNetwork(TwitterAPI twitterAPI, TimelineResponseMapper mapper) {
 this.twitterAPI = twitterAPI;
 this.mapper = mapper;
 }
 
 @Override
 public Observable<Timeline> getTimeline() {
 return twitterAPI
 .getTimeline()
 .retryWhen(new RetryWithDelay(3, 500))
 .map(timelineResponseResult -> mapper.toTimeline(timelineResponseResult.response()));
 }
 
 /* Other methods/implementations */
 } Repository Data source Network Disk DB Memory Undabot
  72. Network implementation public class TimeLineDataSourceNetwork implements TimeLineDataSource {
 
 private

    final TwitterAPI twitterAPI;
 private final TimelineResponseMapper mapper;
 
 public TimeLineDataSourceNetwork(TwitterAPI twitterAPI, TimelineResponseMapper mapper) {
 this.twitterAPI = twitterAPI;
 this.mapper = mapper;
 }
 
 @Override
 public Observable<Timeline> getTimeline() {
 return twitterAPI
 .getTimeline()
 .retryWhen(new RetryWithDelay(3, 500))
 .map(timelineResponseResult -> mapper.toTimeline(timelineResponseResult.response()));
 }
 
 /* Other methods/implementations */
 } public class TimeLineDataSourceNetwork implements TimeLineDataSource {
 
 private final TwitterAPI twitterAPI;
 private final TimelineResponseMapper mapper;
 
 public TimeLineDataSourceNetwork(TwitterAPI twitterAPI, TimelineResponseMapper mapper) {
 this.twitterAPI = twitterAPI;
 this.mapper = mapper;
 }
 
 @Override
 public Observable<Timeline> getTimeline() {
 return twitterAPI
 .getTimeline()
 .retryWhen(new RetryWithDelay(3, 500))
 .map(timelineResponseResult -> mapper.toTimeline(timelineResponseResult.response()));
 }
 
 /* Other methods/implementations */
 } Repository Data source Network Disk DB Memory Undabot
  73. Network implementation public interface TwitterAPI {
 
 @GET("statuses/home_timeline")
 Observable<Response<TimelineResponse>> getTimeline();


    
 /* Other APIs */
 } public class TimeLineDataSourceNetwork implements TimeLineDataSource {
 
 private final TwitterAPI twitterAPI;
 private final TimelineResponseMapper mapper;
 
 public TimeLineDataSourceNetwork(TwitterAPI twitterAPI, TimelineResponseMapper mapper) {
 this.twitterAPI = twitterAPI;
 this.mapper = mapper;
 }
 
 @Override
 public Observable<Timeline> getTimeline() {
 return twitterAPI
 .getTimeline()
 .retryWhen(new RetryWithDelay(3, 500))
 .map(timelineResponseResult -> mapper.toTimeline(timelineResponseResult.response()));
 }
 
 /* Other methods/implementations */
 } public class TimeLineDataSourceNetwork implements TimeLineDataSource {
 
 private final TwitterAPI twitterAPI;
 private final TimelineResponseMapper mapper;
 
 public TimeLineDataSourceNetwork(TwitterAPI twitterAPI, TimelineResponseMapper mapper) {
 this.twitterAPI = twitterAPI;
 this.mapper = mapper;
 }
 
 @Override
 public Observable<Timeline> getTimeline() {
 return twitterAPI
 .getTimeline()
 .retryWhen(new RetryWithDelay(3, 500))
 .map(timelineResponseResult -> mapper.toTimeline(timelineResponseResult.response()));
 }
 
 /* Other methods/implementations */
 } Repository Data source Network Disk DB Memory Undabot
  74. public class TimelineRepositoryImpl implements TimelineRepository {
 
 private final TimeLineDataSourceNetwork

    networkDataSource;
 private final TimeLineDataSourceDisk diskDataSource;
 private final TimeLineDataSourceMem memoryDataSource;
 
 @Inject
 public TimelineRepositoryImpl(TimeLineDataSourceNetwork networkDataSource,
 TimeLineDataSourceDisk diskDataSource,
 TimeLineDataSourceMem memoryDataSource) {
 this.networkDataSource = networkDataSource;
 this.diskDataSource = diskDataSource;
 this.memoryDataSource = memoryDataSource;
 }
 
 @Override
 public Observable<Timeline> getTimeline() {
 return Observable
 .concat(memory(), diskWithSave(), networkWithSave())
 .first(timeline -> timeline != null);
 }
 
 private Observable<Timeline> memory() {
 return memoryDataSource.timeline();
 }
 
 private Observable<Timeline> diskWithSave() {
 return diskDataSource
 .timeline()
 .doOnNext(memoryDataSource::persist); // Save data to memory
 }
 
 private Observable<Timeline> networkWithSave() {
 return networkDataSource.timeline()
 .doOnNext(timeline -> {
 diskDataSource.persist(timeline); // Save data to disk
 memoryDataSource.persist(timeline); // Save data to memory
 });
 }
 
 /* Other methods/implementations */
 } Repository Data source Network Disk DB Memory Undabot
  75. public class TimelineRepositoryImpl implements TimelineRepository {
 
 private final TimeLineDataSourceNetwork

    networkDataSource;
 private final TimeLineDataSourceDisk diskDataSource;
 private final TimeLineDataSourceMem memoryDataSource;
 
 @Inject
 public TimelineRepositoryImpl(TimeLineDataSourceNetwork networkDataSource,
 TimeLineDataSourceDisk diskDataSource,
 TimeLineDataSourceMem memoryDataSource) {
 this.networkDataSource = networkDataSource;
 this.diskDataSource = diskDataSource;
 this.memoryDataSource = memoryDataSource;
 }
 
 @Override
 public Observable<Timeline> getTimeline() {
 return Observable
 .concat(memory(), diskWithSave(), networkWithSave())
 .first(timeline -> timeline != null);
 }
 
 private Observable<Timeline> memory() {
 return memoryDataSource.timeline();
 }
 
 private Observable<Timeline> diskWithSave() {
 return diskDataSource
 .timeline()
 .doOnNext(memoryDataSource::persist); // Save data to memory
 }
 
 private Observable<Timeline> networkWithSave() {
 return networkDataSource.timeline()
 .doOnNext(timeline -> {
 diskDataSource.persist(timeline); // Save data to disk
 memoryDataSource.persist(timeline); // Save data to memory
 });
 }
 
 /* Other methods/implementations */
 } Repository Data source Network Disk DB Memory Undabot
  76. public class TimelineRepositoryImpl implements TimelineRepository {
 
 private final TimeLineDataSourceNetwork

    networkDataSource;
 private final TimeLineDataSourceDisk diskDataSource;
 private final TimeLineDataSourceMem memoryDataSource;
 
 @Inject
 public TimelineRepositoryImpl(TimeLineDataSourceNetwork networkDataSource,
 TimeLineDataSourceDisk diskDataSource,
 TimeLineDataSourceMem memoryDataSource) {
 this.networkDataSource = networkDataSource;
 this.diskDataSource = diskDataSource;
 this.memoryDataSource = memoryDataSource;
 }
 
 @Override
 public Observable<Timeline> getTimeline() {
 return Observable
 .concat(memory(), diskWithSave(), networkWithSave())
 .first(timeline -> timeline != null);
 }
 
 private Observable<Timeline> memory() {
 return memoryDataSource.timeline();
 }
 
 private Observable<Timeline> diskWithSave() {
 return diskDataSource
 .timeline()
 .doOnNext(memoryDataSource::persist); // Save data to memory
 }
 
 private Observable<Timeline> networkWithSave() {
 return networkDataSource.timeline()
 .doOnNext(timeline -> {
 diskDataSource.persist(timeline); // Save data to disk
 memoryDataSource.persist(timeline); // Save data to memory
 });
 }
 
 /* Other methods/implementations */
 } Repository Data source Network Disk DB Memory Undabot
  77. public class TimelineRepositoryImpl implements TimelineRepository {
 
 private final TimeLineDataSourceNetwork

    networkDataSource;
 private final TimeLineDataSourceDisk diskDataSource;
 private final TimeLineDataSourceMem memoryDataSource;
 
 @Inject
 public TimelineRepositoryImpl(TimeLineDataSourceNetwork networkDataSource,
 TimeLineDataSourceDisk diskDataSource,
 TimeLineDataSourceMem memoryDataSource) {
 this.networkDataSource = networkDataSource;
 this.diskDataSource = diskDataSource;
 this.memoryDataSource = memoryDataSource;
 }
 
 @Override
 public Observable<Timeline> getTimeline() {
 return Observable
 .concat(memory(), diskWithSave(), networkWithSave())
 .first(timeline -> timeline != null);
 }
 
 private Observable<Timeline> memory() {
 return memoryDataSource.timeline();
 }
 
 private Observable<Timeline> diskWithSave() {
 return diskDataSource
 .timeline()
 .doOnNext(memoryDataSource::persist); // Save data to memory
 }
 
 private Observable<Timeline> networkWithSave() {
 return networkDataSource.timeline()
 .doOnNext(timeline -> {
 diskDataSource.persist(timeline); // Save data to disk
 memoryDataSource.persist(timeline); // Save data to memory
 });
 }
 
 /* Other methods/implementations */
 } Repository Data source Network Disk DB Memory Undabot
  78. public class TimelineRepositoryImpl implements TimelineRepository {
 
 private final TimeLineDataSourceNetwork

    networkDataSource;
 private final TimeLineDataSourceDisk diskDataSource;
 private final TimeLineDataSourceMem memoryDataSource;
 
 @Inject
 public TimelineRepositoryImpl(TimeLineDataSourceNetwork networkDataSource,
 TimeLineDataSourceDisk diskDataSource,
 TimeLineDataSourceMem memoryDataSource) {
 this.networkDataSource = networkDataSource;
 this.diskDataSource = diskDataSource;
 this.memoryDataSource = memoryDataSource;
 }
 
 @Override
 public Observable<Timeline> getTimeline() {
 return Observable
 .concat(memory(), diskWithSave(), networkWithSave())
 .first(timeline -> timeline != null);
 }
 
 private Observable<Timeline> memory() {
 return memoryDataSource.timeline();
 }
 
 private Observable<Timeline> diskWithSave() {
 return diskDataSource
 .timeline()
 .doOnNext(memoryDataSource::persist); // Save data to memory
 }
 
 private Observable<Timeline> networkWithSave() {
 return networkDataSource.timeline()
 .doOnNext(timeline -> {
 diskDataSource.persist(timeline); // Save data to disk
 memoryDataSource.persist(timeline); // Save data to memory
 });
 }
 
 /* Other methods/implementations */
 } Repository Data source Network Disk DB Memory Undabot
  79. public class TimelineRepositoryImpl implements TimelineRepository {
 
 private final TimeLineDataSourceNetwork

    networkDataSource;
 private final TimeLineDataSourceDisk diskDataSource;
 private final TimeLineDataSourceMem memoryDataSource;
 
 @Inject
 public TimelineRepositoryImpl(TimeLineDataSourceNetwork networkDataSource,
 TimeLineDataSourceDisk diskDataSource,
 TimeLineDataSourceMem memoryDataSource) {
 this.networkDataSource = networkDataSource;
 this.diskDataSource = diskDataSource;
 this.memoryDataSource = memoryDataSource;
 }
 
 @Override
 public Observable<Timeline> getTimeline() {
 return Observable
 .concat(memory(), diskWithSave(), networkWithSave())
 .first(timeline -> timeline != null);
 }
 
 private Observable<Timeline> memory() {
 return memoryDataSource.timeline();
 }
 
 private Observable<Timeline> diskWithSave() {
 return diskDataSource
 .timeline()
 .doOnNext(memoryDataSource::persist); // Save data to memory
 }
 
 private Observable<Timeline> networkWithSave() {
 return networkDataSource.timeline()
 .doOnNext(timeline -> {
 diskDataSource.persist(timeline); // Save data to disk
 memoryDataSource.persist(timeline); // Save data to memory
 });
 }
 
 /* Other methods/implementations */
 } Repository Data source Network Disk DB Memory Undabot
  80. Data mapping public class Tweet {
 
 private long id;


    private String author;
 private String content;
 private String createdAt;
 private boolean favourited;
 private int retweetCount;
 
 
 /* Constructors, getters, setters, methods... */ } Undabot
  81. Data mapping public class Tweet {
 
 private long id;


    private String author;
 @SerializedName("text")
 private String content;
 @SerializedName(value = "created_at", alternate = {"created"})
 private String createdAt;
 private boolean favourited;
 @SerializedName("retweet_count")
 private int retweetCount;
 
 public Tweet() {
 }
 
 
 /* Constructors, getters, setters, methods... */
 } Undabot
  82. Data mapping @Table(database = TwitterDatabse.class)
 public class Tweet extends BaseModel

    {
 
 @PrimaryKey
 private long id;
 @Column
 private String author;
 @Column
 @SerializedName("text")
 private String content;
 @Column
 @SerializedName(value = "created_at", alternate = {"created"})
 private String createdAt;
 @Column
 private boolean favourited;
 @Column
 @SerializedName("retweet_count")
 private int retweetCount;
 
 public Tweet() {
 }
 
 
 /* Constructors, getters, setters, methods... */
 } Undabot
  83. Data mapping Tweet ApiTweet DbTweet Response model Response Mapper ApiMapper

    DbMapper class TimelineResultMapper implements Func1<Result<TimelineResponse>, List<ApiTweet>> Undabot
  84. Data mapping Tweet ApiTweet DbTweet Response model Response Mapper ApiMapper

    DbMapper class TimelineResultMapper implements Func1<Result<TimelineResponse>, List<ApiTweet>> .map(twitterResponseMapper); Undabot
  85. Swappable implementations View Presenter UseCase Abstraction Model Business logic Repository

    Data source Location provider Image loader Glide Payment processor Pull to refresh Native time pickers Continuous location Production payment Network ORMLite Undabot
  86. Swappable implementations View Presenter UseCase Abstraction Model Business logic Repository

    Data source Location provider Image loader Glide Payment processor Pull to refresh Swipe refresh Native time pickers Continuous location Production payment Network ORMLite Undabot
  87. Swappable implementations View Presenter UseCase Abstraction Model Business logic Repository

    Data source Location provider Image loader Glide Payment processor Pull to refresh Swipe refresh Native time pickers Better Pickers Continuous location Production payment Network ORMLite Undabot
  88. Swappable implementations View Presenter UseCase Abstraction Model Business logic Repository

    Data source Location provider Image loader Glide Payment processor Pull to refresh Swipe refresh Native time pickers Better Pickers Picasso Continuous location Production payment Network ORMLite Undabot
  89. Swappable implementations View Presenter UseCase Abstraction Model Business logic Repository

    Data source Location provider Image loader Glide Payment processor Pull to refresh Swipe refresh Native time pickers Better Pickers Picasso Continuous location Fixed location Production payment Network ORMLite Undabot
  90. Swappable implementations View Presenter UseCase Abstraction Model Business logic Repository

    Data source Location provider Image loader Glide Payment processor Pull to refresh Swipe refresh Native time pickers Better Pickers Picasso Continuous location Fixed location Production payment Network ORMLite DBFlow Undabot
  91. Swappable implementations View Presenter UseCase Abstraction Model Business logic Repository

    Data source Location provider Image loader Glide Payment processor Pull to refresh Swipe refresh Native time pickers Better Pickers Picasso Continuous location Fixed location Production payment Fake payment Network ORMLite DBFlow Undabot
  92. Swappable implementations View Presenter UseCase Abstraction Model Business logic Repository

    Data source Location provider Image loader Glide Payment processor Pull to refresh Swipe refresh Native time pickers Better Pickers Picasso Continuous location Fixed location Production payment Fake payment Network Local mock ORMLite DBFlow Undabot
  93. Packaging p a c k a g e s presentation

    feature A
 feature B
 feature C domain data l a y e r s Package by layer Undabot
  94. Packaging p a c k a g e s presentation

    feature A
 feature B
 feature C domain data l a y e r s Package by layer feature A feature B feature C l a y e r s Package by feature Undabot
  95. Packaging p a c k a g e s packages

    presentation feature A
 feature B
 feature C domain data l a y e r s Package by layer feature A feature B feature C l a y e r s Package by feature Undabot
  96. Package by layer data (module)
 └───repository
 └───cache
 └───database
 └───api
 └───exception


    └───shared
 
 domain (module)
 └───model
 └───interactor
 └───executor
 └───abstraction
 └───shared
 
 presentation (module)
 └───presenter
 └───view
 └───navigation
 └───shared Undabot
  97. Package by layer data (module)
 └───repository
 └───cache
 └───database
 └───api
 └───exception


    └───shared
 
 domain (module)
 └───model
 └───interactor
 └───executor
 └───abstraction
 └───shared
 
 presentation (module)
 └───presenter
 └───view
 └───navigation
 └───shared apply plugin: 'java' apply plugin: 'com.android.library'
 … compile project(':domain') apply plugin: ‘com.android.application' … compile project(':domain')
 compile project(':data') Undabot
  98. Package by layer data (module)
 └───repository
 └───cache
 └───database
 └───api
 └───exception


    └───shared
 
 domain (module)
 └───model
 └───interactor
 └───executor
 └───abstraction
 └───shared
 
 presentation (module)
 └───presenter
 └───view
 └───navigation
 └───shared app (module)
 └───data
 └───repository
 └───cache
 └───database
 └───api
 └───exception
 └───shared
 └───domain
 └───model
 └───interactor
 └───executor
 └───abstraction
 └───shared
 └───presentation
 └───presenter
 └───view
 └───navigation
 └───shared Undabot
  99. Package by layer data (module)
 └───repository
 └───cache
 └───database
 └───api
 └───exception


    └───shared
 
 domain (module)
 └───model
 └───interactor
 └───executor
 └───abstraction
 └───shared
 
 presentation (module)
 └───presenter
 └───view
 └───navigation
 └───shared app (module)
 └───data
 └───repository
 └───cache
 └───database
 └───api
 └───exception
 └───shared
 └───domain
 └───model
 └───interactor
 └───executor
 └───abstraction
 └───shared
 └───presentation
 └───presenter
 └───view
 └───navigation
 └───shared Undabot
  100. Package by feature app
 └───core
 └───data
 └───utils
 └───timeline
 └───details
 └───gallery


    └───post
 └───profile
 └───notifications
 └───messaging
 └───metrics
 └───search
 └───settings Undabot
  101. Package by feature app
 └───core
 └───data
 └───utils
 └───timeline
 └───details
 └───gallery


    └───post
 └───profile
 └───notifications
 └───messaging
 └───metrics
 └───search
 └───settings C C C C C C C C C C C profile gallery Undabot