class Wiring { private final Observable viewModelObservable; Wiring(View view, ActionDispatcher actionDispatcher, Service service, ViewModelScanner viewModelScanner) { viewModelObservable = view.uiEventObservable() .compose(actionDispatcher.uiEventToAction()) .compose(service.actionToResult()) .compose(viewModelScanner.resultToViewModel()); } public Observable viewModelObservable() { return viewModelObservable; } } interface View { Observable uiEventObservable(); } class ActionDispatcher { ObservableTransformer uiEventToAction() { ObservableTransformer textUpdatedToAction = events -> events .flatMap(textUpdated -> Observable.just(new GetAutocomplete(textUpdated.text))); ObservableTransformer submitClickedToAction = events -> events .flatMap(submitClicked -> Observable.just(new Submit(), new SendAnalytics("submit"))); return events -> events .publish(shared -> Observable.merge( shared.ofType(TextUpdated.class).compose(textUpdatedToAction), shared.ofType(SubmitClicked.class).compose(submitClickedToAction) )); } } class Service { private final AutocompleteService autocompleteService; private final SubmitService submitService; public Service(AutocompleteService autocompleteService, SubmitService submitService) { this.autocompleteService = autocompleteService; this.submitService = submitService; } ObservableTransformer actionToResult() { return events -> events .publish(shared -> Observable.merge( shared.ofType(GetAutocomplete.class).compose(autocompleteService.transformer()), shared.ofType(Submit.class).compose(submitService.transformer()) )); } } class SubmitService { ObservableTransformer transformer() { return events -> events .flatMap(submit -> { return Observable.just(SubmitResult.IN_FLIGHT, SubmitResult.SUCCESS); }); } } class AutocompleteService { ObservableTransformer transformer() { return events -> events .flatMap(getAutocomplete -> { String[] results = new String[3]; results[0] = getAutocomplete.text + "a"; results[1] = getAutocomplete.text + "b"; results[2] = getAutocomplete.text + "c"; return Observable.just( AutoCompleteResult.IN_FLIGHT, AutoCompleteResult.success(results) ); }); } } class ViewModelScanner { ObservableTransformer resultToViewModel() { return events -> events .scan(ViewModel.initial(), (viewModel, result) -> { viewModel = viewModel.copy(); viewModel.isLoading = (result == AutoCompleteResult.IN_FLIGHT || result == SubmitResult.IN_FLIGHT); viewModel.submitButtonClickable = result != SubmitResult.IN_FLIGHT; if (result == AutoCompleteResult.ERROR || result == SubmitResult.ERROR) { viewModel.autocompleteOptions = null; viewModel.text = "ERROR!"; } else if (result instanceof AutocompleteSuccessResult) { viewModel.autocompleteOptions = ((AutocompleteSuccessResult) result).autocompletes; } else if (result == SubmitResult.SUCCESS) { viewModel.text = "SUCCESS!"; } return viewModel; }) .cache(); } } abstract class UiEvent { static class TextUpdated extends UiEvent { final String text; TextUpdated(String text) { this.text = text; } } static class SubmitClicked extends UiEvent { } } class ViewModel { public static ViewModel initial() { return new ViewModel(); } String text; String[] autocompleteOptions; boolean submitButtonClickable = true; boolean isLoading; ViewModel copy() { ViewModel result = new ViewModel(); result.text = text; result.autocompleteOptions = autocompleteOptions; result.submitButtonClickable = submitButtonClickable; result.isLoading = isLoading; return result; } } abstract class Action { static class GetAutocomplete extends Action { final String text; GetAutocomplete(String text) { this.text = text; } } static class Submit extends Action { } static class SendAnalytics extends Action { final String event; SendAnalytics(String event) { this.event = event; } } } abstract class Result { static class AutoCompleteResult extends Result { public static final AutoCompleteResult IN_FLIGHT = new AutoCompleteResult(); public static final AutoCompleteResult ERROR = new AutoCompleteResult(); public static AutocompleteSuccessResult success(String... autocompletes) { return new AutocompleteSuccessResult(autocompletes); } static class AutocompleteSuccessResult extends AutoCompleteResult { final String[] autocompletes; AutocompleteSuccessResult(String[] autocompletes) { this.autocompletes = autocompletes; } } } static class SubmitResult extends Result { public static final SubmitResult IN_FLIGHT = new SubmitResult(); public static final SubmitResult SUCCESS = new SubmitResult(); public static final SubmitResult ERROR = new SubmitResult(); } }