Slide 1

Slide 1 text

Android VIPER - MVVM Hybrid Architecture

Slide 2

Slide 2 text

About me Marius Constantin Android Architect @ Deezer

Slide 3

Slide 3 text

Deezer Backstage 50 180 700 30 000 6 M. 40 M. 100 M. 1 Billion 1.5 Tera 4.5 Peta Deezer Editors Countries Dedicated Servers New Albums Each Week Paying Subscribers Titles Playlists Requests Per Day Logs Per Day Bytes

Slide 4

Slide 4 text

Why ? Deezer Android App Code More Unit Tests Feature More Functional Tests Feature Feature Refactor and extract to Core SDK Bug Fixing 700, 000 lines of legacy and new code

Slide 5

Slide 5 text

Content - From where we have started and where we are right now - VIPER - MVVM architecture - Demo application using the new architecture - Why using Reactive Programming can bring you more flexibility

Slide 6

Slide 6 text

Where we were and where we are

Slide 7

Slide 7 text

The road to MVP The old faithful MVC MODEL VIEW CONTROLLER Fetch data from NETWORK Fetch data from Local Storage Transform the data Persist the data Deliver the data etc. Visual rendering Is stateless Not coupled directly to MODEL Builds the VIEW Navigation Controls the MODEL Handling system events

Slide 8

Slide 8 text

MVC to MVP The VIEW takes some of the responsibilities from the Presenter MODEL VIEW Supervising Controller variation PRESENTER Fetch data from NETWORK Fetch data from Local Storage Transform data Persist data Deliver data etc. Visual representation of MODEL Direct coupling to MODEL through data binding Rendering MODEL data updates Saves state Navigation Delivers MODEL data to the VIEW Controls the MODEL Handling system events Modifies MODEL requests based on system information

Slide 9

Slide 9 text

MVP Diagram Presenter Data Model VIEW Click Event Handle Click Renders the Model Data With DataBinding Navigate to different VIEW Handle navigation

Slide 10

Slide 10 text

VIPER - MVVM Hybrid Architecture

Slide 11

Slide 11 text

Why VIPER - MVVM ? The MVP behaves badly when the feature scales up It ends up with lots responsibilities and lines of code Decouple more Add more Unit Tests Have a more adaptable and permissive architecture Adapt your architecture for Functional Testing

Slide 12

Slide 12 text

Feature Presenter Add ons to the feature Add more code More code... More...

Slide 13

Slide 13 text

We start from the MODEL MODEL Fetch data from NETWORK Fetch data from Local Storage Transform data Persist data Deliver data etc. Repository Entities

Slide 14

Slide 14 text

Extract the interactor from the Presenter PRESENTER Delivers data to the VIEW Controls the MODEL Handling system events Modifies MODEL requests based on system information INTERACTOR Controls the Repository Handling system events Modifies Repository requests based on system information Transforms data

Slide 15

Slide 15 text

Introducing the ViewModel Change the Presenter’s responsibilities PRESENTER Fetch the Value Objects by using Interactor Construct the ViewModel Deliver the ViewModel to the View (recommended using Dependency Inversion)

Slide 16

Slide 16 text

Extract the FlowController from the View VIEW Render the ViewModel Saves the State through ViewModel Navigation FLOW CONTROLLER Navigation

Slide 17

Slide 17 text

VIPER - MVVM by responsibilities PRESENTER INTERACTOR REPOSITORY System Events (LRU cache events, Network Events, etc) VIEW FLOW CONTROLLER VIEW MODEL ViewModel ValueObject Depends on

Slide 18

Slide 18 text

VIEW ViewModel FlowController onClick...setTitleColor() Notify the View that a property was changed in order to render itself. Navigate to a different View onClick...modify the Repository Interactor Repo Modifies the Repo Delivers a new ViewModel Presenter Delivers a new ValueObject DataBinding View Workflow

Slide 19

Slide 19 text

Taken from: https://www.objc.io/issues/13-architecture/viper/

Slide 20

Slide 20 text

Demo Application

Slide 21

Slide 21 text

https://api.deezer.com/playlist/{playlistId}/tracks Fetch a JSON object containing a list of tracks Render the tracks using a RecyclerView “onTrackClick” event go to the detailed view of the track

Slide 22

Slide 22 text

Data Layer - ApiService (Retrofit + RxJava Adapter) public interface IApiService { String BASE_API_URL="https://api.deezer.com"; @GET("/playlist/{playlistId}/tracks") Observable getTracks( @Path("playlistId") long playlistId); }

Slide 23

Slide 23 text

Data Layer - Repository public class PlaylistRepository implements IPlaylistRepository { @NetworkPlaylistRepo private final IPlaylistRepository mNetworkRepository; @LocalPlaylistRepo private final IPlaylistRepository mLocalRepository; @Override public Observable getPlaylist( long id) { … }

Slide 24

Slide 24 text

Model Layer - PlaylistInteractor public class PlaylistInteractor implements IPlaylistInteractor { @NonNull private final IPlaylistRepository mPlaylistRepository; public PlaylistInteractor( @NonNull IPlaylistRepository playlistRepository) { mPlaylistRepository = playlistRepository; } @Override public Observable getPlaylistObservable( long playlistId) { // add more transformations if needed return mPlaylistRepository.getPlaylist(playlistId); } The interactor can listen to System events like “onConnectionChanged” or “onTrimMemory”

Slide 25

Slide 25 text

View Layer - View public class PlaylistFragment extends Fragment implements IPlaylistContract.IPlaylistView { @Inject IPlaylistContract.IPlaylistPresenter mPresenter; @Inject FlowController mFlowController; ... @Override public void goToTrackDetails( @NonNull Track track) { mFlowController.goToTrackDetails(track, getActivity()); ...

Slide 26

Slide 26 text

View Layer - View private final Action1 mOnNext = new Action1() { @Override public void call(PlaylistViewModel model) { mPlaylistAdapter.setData(model.tracks()); } } @Override public void onResume() { super.onResume(); mViewModelSubscription = mPresenter.onResume(mOnNext, getArguments().getLong( PLAYLIST_ID_KEY)); } ...

Slide 27

Slide 27 text

View Layer - PlaylistViewModel @AutoValue public abstract class PlaylistViewModel implements Parcelable { public abstract List tracks(); ….

Slide 28

Slide 28 text

View Layer - PlaylistFlowController @ActivityScope public class PlaylistFlowController { @Inject private final FragmentActivity mFragmentActivity; @Inject public PlaylistFlowController() {} public void goToTrackDetails(@NonNull Track track) { switchToFragment(TrackDetailFragment.create(track), mFragmentActivity.getSupportFragmentManager()); }

Slide 29

Slide 29 text

View Layer - PlaylistPresenter public class PlaylistPresenter implements IPlaylistContract.IPlaylistPresenter { @NonNull private final IPlaylistInteractor mPlaylistInteractor; @NonNull private PlaylistViewModel mPlaylistViewModel; @Override public Subscription onResume(Action1 consumer, long playlistId) { if (mPlaylistViewModel != null) { mViewModelBehaviorSubject.onNext(mPlaylistViewModel); } else { refreshData(playlistId); }

Slide 30

Slide 30 text

[Dagger 2] - http://google.github.io/dagger/subcomponents.html [RxJava] - https://github.com/ReactiveX/RxJava/wiki [Retrofit] - https://square.github.io/retrofit/ [Data Binding] - https://developer.android.com/topic/libraries/data-binding/index.html [CourseraDemo Viper Architecture] by Richa Khandelwal - https://github.com/richk/CourseraDemoApp https://twitter.com/mariusc83 https://github.com/mariusc83 https://github.com/mariusc83/patterns (Demo App) [email protected] References

Slide 31

Slide 31 text

Why Go Reactive ?