EVOLUINDO ARQUITETURAS REATIVAS - QConSP · RxJava permite operações assíncronas em um estilo...
Transcript of EVOLUINDO ARQUITETURAS REATIVAS - QConSP · RxJava permite operações assíncronas em um estilo...
![Page 1: EVOLUINDO ARQUITETURAS REATIVAS - QConSP · RxJava permite operações assíncronas em um estilo síncrono, turbinadas por operadores funcionais Threading transparente Tratamento](https://reader035.fdocuments.net/reader035/viewer/2022062916/5ec675b5338f896c77290e1a/html5/thumbnails/1.jpg)
EVOLUINDO ARQUITETURAS
REATIVASUbiratan Soares QCONSP / 2017
![Page 2: EVOLUINDO ARQUITETURAS REATIVAS - QConSP · RxJava permite operações assíncronas em um estilo síncrono, turbinadas por operadores funcionais Threading transparente Tratamento](https://reader035.fdocuments.net/reader035/viewer/2022062916/5ec675b5338f896c77290e1a/html5/thumbnails/2.jpg)
O QUE É UMA ARQUITETURA EM
MOBILE ?
![Page 3: EVOLUINDO ARQUITETURAS REATIVAS - QConSP · RxJava permite operações assíncronas em um estilo síncrono, turbinadas por operadores funcionais Threading transparente Tratamento](https://reader035.fdocuments.net/reader035/viewer/2022062916/5ec675b5338f896c77290e1a/html5/thumbnails/3.jpg)
MVP
MVVMVIPER
FLUX REDUX
CLEAN
MVC…
MVI
![Page 4: EVOLUINDO ARQUITETURAS REATIVAS - QConSP · RxJava permite operações assíncronas em um estilo síncrono, turbinadas por operadores funcionais Threading transparente Tratamento](https://reader035.fdocuments.net/reader035/viewer/2022062916/5ec675b5338f896c77290e1a/html5/thumbnails/4.jpg)
![Page 5: EVOLUINDO ARQUITETURAS REATIVAS - QConSP · RxJava permite operações assíncronas em um estilo síncrono, turbinadas por operadores funcionais Threading transparente Tratamento](https://reader035.fdocuments.net/reader035/viewer/2022062916/5ec675b5338f896c77290e1a/html5/thumbnails/5.jpg)
PRINCÍPIOS DE ARQUITETURAOrganização
Facilidade em se encontrar o que se precisa
Menor impedância para se resolver bugs
Menos dor ao escalar em tamanho (codebase e devs)
Estilo de projeto unificado, definido e defendido pelo time
![Page 6: EVOLUINDO ARQUITETURAS REATIVAS - QConSP · RxJava permite operações assíncronas em um estilo síncrono, turbinadas por operadores funcionais Threading transparente Tratamento](https://reader035.fdocuments.net/reader035/viewer/2022062916/5ec675b5338f896c77290e1a/html5/thumbnails/6.jpg)
MOBILE CHALLENGESInterações com usuário e eventos de sistema são assíncronos
I/O deve ser concorrente
Processamento pesado deve ser concorrente
Fragmentação de plataformas
ETC….
![Page 7: EVOLUINDO ARQUITETURAS REATIVAS - QConSP · RxJava permite operações assíncronas em um estilo síncrono, turbinadas por operadores funcionais Threading transparente Tratamento](https://reader035.fdocuments.net/reader035/viewer/2022062916/5ec675b5338f896c77290e1a/html5/thumbnails/7.jpg)
UMA QUEIXA COMUM NA COMUNIDADE
MOBILE ?
![Page 8: EVOLUINDO ARQUITETURAS REATIVAS - QConSP · RxJava permite operações assíncronas em um estilo síncrono, turbinadas por operadores funcionais Threading transparente Tratamento](https://reader035.fdocuments.net/reader035/viewer/2022062916/5ec675b5338f896c77290e1a/html5/thumbnails/8.jpg)
TEM PELO MENOS UM UNIT TEST NO APP?
![Page 9: EVOLUINDO ARQUITETURAS REATIVAS - QConSP · RxJava permite operações assíncronas em um estilo síncrono, turbinadas por operadores funcionais Threading transparente Tratamento](https://reader035.fdocuments.net/reader035/viewer/2022062916/5ec675b5338f896c77290e1a/html5/thumbnails/9.jpg)
EM MOBILE, ARQUITETURA É CRÍTICA
PARA TESTABILIDADE
![Page 10: EVOLUINDO ARQUITETURAS REATIVAS - QConSP · RxJava permite operações assíncronas em um estilo síncrono, turbinadas por operadores funcionais Threading transparente Tratamento](https://reader035.fdocuments.net/reader035/viewer/2022062916/5ec675b5338f896c77290e1a/html5/thumbnails/10.jpg)
QUAL ARQUITETURA ESCOLHER ENTÃO ???
![Page 11: EVOLUINDO ARQUITETURAS REATIVAS - QConSP · RxJava permite operações assíncronas em um estilo síncrono, turbinadas por operadores funcionais Threading transparente Tratamento](https://reader035.fdocuments.net/reader035/viewer/2022062916/5ec675b5338f896c77290e1a/html5/thumbnails/11.jpg)
![Page 12: EVOLUINDO ARQUITETURAS REATIVAS - QConSP · RxJava permite operações assíncronas em um estilo síncrono, turbinadas por operadores funcionais Threading transparente Tratamento](https://reader035.fdocuments.net/reader035/viewer/2022062916/5ec675b5338f896c77290e1a/html5/thumbnails/12.jpg)
![Page 13: EVOLUINDO ARQUITETURAS REATIVAS - QConSP · RxJava permite operações assíncronas em um estilo síncrono, turbinadas por operadores funcionais Threading transparente Tratamento](https://reader035.fdocuments.net/reader035/viewer/2022062916/5ec675b5338f896c77290e1a/html5/thumbnails/13.jpg)
![Page 14: EVOLUINDO ARQUITETURAS REATIVAS - QConSP · RxJava permite operações assíncronas em um estilo síncrono, turbinadas por operadores funcionais Threading transparente Tratamento](https://reader035.fdocuments.net/reader035/viewer/2022062916/5ec675b5338f896c77290e1a/html5/thumbnails/14.jpg)
![Page 15: EVOLUINDO ARQUITETURAS REATIVAS - QConSP · RxJava permite operações assíncronas em um estilo síncrono, turbinadas por operadores funcionais Threading transparente Tratamento](https://reader035.fdocuments.net/reader035/viewer/2022062916/5ec675b5338f896c77290e1a/html5/thumbnails/15.jpg)
MIXED FEELINGS
![Page 16: EVOLUINDO ARQUITETURAS REATIVAS - QConSP · RxJava permite operações assíncronas em um estilo síncrono, turbinadas por operadores funcionais Threading transparente Tratamento](https://reader035.fdocuments.net/reader035/viewer/2022062916/5ec675b5338f896c77290e1a/html5/thumbnails/16.jpg)
![Page 17: EVOLUINDO ARQUITETURAS REATIVAS - QConSP · RxJava permite operações assíncronas em um estilo síncrono, turbinadas por operadores funcionais Threading transparente Tratamento](https://reader035.fdocuments.net/reader035/viewer/2022062916/5ec675b5338f896c77290e1a/html5/thumbnails/17.jpg)
NÃO HÁ SILVER BULLETS!
![Page 18: EVOLUINDO ARQUITETURAS REATIVAS - QConSP · RxJava permite operações assíncronas em um estilo síncrono, turbinadas por operadores funcionais Threading transparente Tratamento](https://reader035.fdocuments.net/reader035/viewer/2022062916/5ec675b5338f896c77290e1a/html5/thumbnails/18.jpg)
MODEL VIEW
PRESENTER
![Page 19: EVOLUINDO ARQUITETURAS REATIVAS - QConSP · RxJava permite operações assíncronas em um estilo síncrono, turbinadas por operadores funcionais Threading transparente Tratamento](https://reader035.fdocuments.net/reader035/viewer/2022062916/5ec675b5338f896c77290e1a/html5/thumbnails/19.jpg)
PRESENTATION LAYER
DATA LAYER
DB
REST
ETC
UI
. . .
![Page 20: EVOLUINDO ARQUITETURAS REATIVAS - QConSP · RxJava permite operações assíncronas em um estilo síncrono, turbinadas por operadores funcionais Threading transparente Tratamento](https://reader035.fdocuments.net/reader035/viewer/2022062916/5ec675b5338f896c77290e1a/html5/thumbnails/20.jpg)
![Page 21: EVOLUINDO ARQUITETURAS REATIVAS - QConSP · RxJava permite operações assíncronas em um estilo síncrono, turbinadas por operadores funcionais Threading transparente Tratamento](https://reader035.fdocuments.net/reader035/viewer/2022062916/5ec675b5338f896c77290e1a/html5/thumbnails/21.jpg)
public interface ViewDelegate {
void displayResults(DataModel model);
void networkingError();
void displayEmptyState();
void displayErrorState();
// More delegation }
![Page 22: EVOLUINDO ARQUITETURAS REATIVAS - QConSP · RxJava permite operações assíncronas em um estilo síncrono, turbinadas por operadores funcionais Threading transparente Tratamento](https://reader035.fdocuments.net/reader035/viewer/2022062916/5ec675b5338f896c77290e1a/html5/thumbnails/22.jpg)
public class MainActivity extends AppCompatActivity implements ViewDelegate {
Presenter presenter; // How to resolve this instance ???
@Override protected void onStart() { super.onStart(); presenter.bindView(this); presenter.fetchData(); }
@Override public void displayResults(DataModel model) { // Put data into view }
@Override public void networkingError() { // Up to you }
@Override public void displayEmptyState() { // And this too! }
@Override public void displayErrorState() { // Please, do not mess with your user } }
![Page 23: EVOLUINDO ARQUITETURAS REATIVAS - QConSP · RxJava permite operações assíncronas em um estilo síncrono, turbinadas por operadores funcionais Threading transparente Tratamento](https://reader035.fdocuments.net/reader035/viewer/2022062916/5ec675b5338f896c77290e1a/html5/thumbnails/23.jpg)
public class Presenter {
public void bindView(ViewDelegate delegate) { this.delegate = delegate; }
public void fetchData() { source.fetchData(new DataSource.Callback() {
@Override public void onDataLoaded(DataModel model) { delegate.displayResults(model); }
@Override public void onError(Throwable t) {
if (t instanceof NetworkingError) { delegate.networkingError(); } else if (t instanceof NoDataAvailable) { … } } }); } }
![Page 24: EVOLUINDO ARQUITETURAS REATIVAS - QConSP · RxJava permite operações assíncronas em um estilo síncrono, turbinadas por operadores funcionais Threading transparente Tratamento](https://reader035.fdocuments.net/reader035/viewer/2022062916/5ec675b5338f896c77290e1a/html5/thumbnails/24.jpg)
DATASOURCE
REST GATEWAY
PRESENTER
VIEW DELEGATION CALLBACKS
PLATAFORM CONTROLLER
CALLBACKUNIT TESTS
(Mocked Contract)
FUNCTIONAL UI TESTS INTEGRATION TESTS
INTEGRATION TESTS (DOUBLES)
UNIT TESTS (Mocked Source
+ Mocked View)DA
TAM
ODE
L
![Page 25: EVOLUINDO ARQUITETURAS REATIVAS - QConSP · RxJava permite operações assíncronas em um estilo síncrono, turbinadas por operadores funcionais Threading transparente Tratamento](https://reader035.fdocuments.net/reader035/viewer/2022062916/5ec675b5338f896c77290e1a/html5/thumbnails/25.jpg)
String description = “Blah” String date = “2010-02-26T19:35:24Z” int step = 2
String description = “Blah” LocalDateTime dateTime = (JSR310) TrackingStep currentStep = (enum)
String description = “Blah” String formattedDate = “26/02/2010” String currentStep = “Concluído”
Response Model
Domain Model
View Model
DATA MODEL
![Page 26: EVOLUINDO ARQUITETURAS REATIVAS - QConSP · RxJava permite operações assíncronas em um estilo síncrono, turbinadas por operadores funcionais Threading transparente Tratamento](https://reader035.fdocuments.net/reader035/viewer/2022062916/5ec675b5338f896c77290e1a/html5/thumbnails/26.jpg)
PROBLEMAS EM POTENCIALQual representação de dados utilizar? Unificada ou separada?
Onde aplicar parsing? E formatação para a UI?
Callbacks aninhados
Memory leaks no nível do mecanismo de entrega
Etc
![Page 27: EVOLUINDO ARQUITETURAS REATIVAS - QConSP · RxJava permite operações assíncronas em um estilo síncrono, turbinadas por operadores funcionais Threading transparente Tratamento](https://reader035.fdocuments.net/reader035/viewer/2022062916/5ec675b5338f896c77290e1a/html5/thumbnails/27.jpg)
BRACE YOURSELVES
RX IS COMING
![Page 28: EVOLUINDO ARQUITETURAS REATIVAS - QConSP · RxJava permite operações assíncronas em um estilo síncrono, turbinadas por operadores funcionais Threading transparente Tratamento](https://reader035.fdocuments.net/reader035/viewer/2022062916/5ec675b5338f896c77290e1a/html5/thumbnails/28.jpg)
THE RXJAVA REVOLUTIONRxJava permite operações assíncronas em um estilo síncrono, turbinadas por operadores funcionais
Threading transparente
Tratamento unificado de erros via adição ao Observer Pattern
Battle-tested
![Page 29: EVOLUINDO ARQUITETURAS REATIVAS - QConSP · RxJava permite operações assíncronas em um estilo síncrono, turbinadas por operadores funcionais Threading transparente Tratamento](https://reader035.fdocuments.net/reader035/viewer/2022062916/5ec675b5338f896c77290e1a/html5/thumbnails/29.jpg)
COMO ADICIONAR RX
NESSA ARQUITETURA ??
![Page 30: EVOLUINDO ARQUITETURAS REATIVAS - QConSP · RxJava permite operações assíncronas em um estilo síncrono, turbinadas por operadores funcionais Threading transparente Tratamento](https://reader035.fdocuments.net/reader035/viewer/2022062916/5ec675b5338f896c77290e1a/html5/thumbnails/30.jpg)
PRESENTATION LAYER
DATA LAYER
DB
REST
ETC
UI
. . .
Callback(T)Callback(T) Callback(T)
![Page 31: EVOLUINDO ARQUITETURAS REATIVAS - QConSP · RxJava permite operações assíncronas em um estilo síncrono, turbinadas por operadores funcionais Threading transparente Tratamento](https://reader035.fdocuments.net/reader035/viewer/2022062916/5ec675b5338f896c77290e1a/html5/thumbnails/31.jpg)
SUBSTITUIR CALLBACKS POR SEQUÊNCIAS OBSERVÁVEIS
![Page 32: EVOLUINDO ARQUITETURAS REATIVAS - QConSP · RxJava permite operações assíncronas em um estilo síncrono, turbinadas por operadores funcionais Threading transparente Tratamento](https://reader035.fdocuments.net/reader035/viewer/2022062916/5ec675b5338f896c77290e1a/html5/thumbnails/32.jpg)
PRIMEIRA INTERAÇÃO
CAMADA DE DADOS REATIVA
![Page 33: EVOLUINDO ARQUITETURAS REATIVAS - QConSP · RxJava permite operações assíncronas em um estilo síncrono, turbinadas por operadores funcionais Threading transparente Tratamento](https://reader035.fdocuments.net/reader035/viewer/2022062916/5ec675b5338f896c77290e1a/html5/thumbnails/33.jpg)
REST GATEWAY
VIEW DELEGATION
VIEW
DATA SOURCE
Observable<T>
PRESENTER
Callback(T)
OBSERVER<T>
![Page 34: EVOLUINDO ARQUITETURAS REATIVAS - QConSP · RxJava permite operações assíncronas em um estilo síncrono, turbinadas por operadores funcionais Threading transparente Tratamento](https://reader035.fdocuments.net/reader035/viewer/2022062916/5ec675b5338f896c77290e1a/html5/thumbnails/34.jpg)
public interface HelpDeskEventsSource {
Observable<HelpDeskEvent> fetchWith(MessagesForOrderParameters params);
Observable<HelpDeskEvent> sendMessage(MessageToSellerParameters params);
Observable<HelpDeskEvent> requireMediation(MediationParameters params);
}
ADEUS CALLBACKS !!!👌
![Page 35: EVOLUINDO ARQUITETURAS REATIVAS - QConSP · RxJava permite operações assíncronas em um estilo síncrono, turbinadas por operadores funcionais Threading transparente Tratamento](https://reader035.fdocuments.net/reader035/viewer/2022062916/5ec675b5338f896c77290e1a/html5/thumbnails/35.jpg)
public class HelpDeskStreamInfrastructure implements HelpDeskEventsSource {
@Override public Observable<HelpDeskEvent> fetchWith(MessagesForOrderParameters params) { return restAPI.getHelpDeskTickets(params) .subscribeOn(Schedulers.io()) .map(HelpDeskPayloadMapper::map) .filter(Preconditions::notNullOrEmpty) .flatMap(Observable::from); }
@Override public Observable<HelpDeskEvent> sendMessage(MessageToSellerParameters params) { MessageToSellerBody body = SendMessageToSellerBodyMapper.convert(params);
return restAPI.sendHelpdeskMessageToSeller(body) .subscribeOn(Schedulers.io()) .flatMap(emptyBody -> fetchWith(sameFromSeller(params))); }
} Chained request, easy !!!!
![Page 36: EVOLUINDO ARQUITETURAS REATIVAS - QConSP · RxJava permite operações assíncronas em um estilo síncrono, turbinadas por operadores funcionais Threading transparente Tratamento](https://reader035.fdocuments.net/reader035/viewer/2022062916/5ec675b5338f896c77290e1a/html5/thumbnails/36.jpg)
VANTAGENS OBSERVADASFacilidades via frameworks utilitários para REST / DB
Validação de dados de entrada e tradução de modelos como etapas do pipeline
Tratamento de erros, auto retry, exponential backoff no “baixo nível”
![Page 37: EVOLUINDO ARQUITETURAS REATIVAS - QConSP · RxJava permite operações assíncronas em um estilo síncrono, turbinadas por operadores funcionais Threading transparente Tratamento](https://reader035.fdocuments.net/reader035/viewer/2022062916/5ec675b5338f896c77290e1a/html5/thumbnails/37.jpg)
PROBLEMAS OBSERVADOSConsumir os dados no nível da apresentação nos força a rodar comportamentos na thread principal do app (orquestração dos callbacks)
Indireção forçada para prover Scheduler via DI, para propósitos de testes
Muitas responsabilidades no Presenter
![Page 38: EVOLUINDO ARQUITETURAS REATIVAS - QConSP · RxJava permite operações assíncronas em um estilo síncrono, turbinadas por operadores funcionais Threading transparente Tratamento](https://reader035.fdocuments.net/reader035/viewer/2022062916/5ec675b5338f896c77290e1a/html5/thumbnails/38.jpg)
SEGUNDA INTERAÇÃO
CAMADA DE APRESENTAÇÃO REATIVA
![Page 39: EVOLUINDO ARQUITETURAS REATIVAS - QConSP · RxJava permite operações assíncronas em um estilo síncrono, turbinadas por operadores funcionais Threading transparente Tratamento](https://reader035.fdocuments.net/reader035/viewer/2022062916/5ec675b5338f896c77290e1a/html5/thumbnails/39.jpg)
REST GATEWAY
VIEW DELEGATION
VIEW
DATA SOURCE
Observable<T>
PRESENTER
OBSERVER<T>
Observable<T>
SUBSCRIPTION
![Page 40: EVOLUINDO ARQUITETURAS REATIVAS - QConSP · RxJava permite operações assíncronas em um estilo síncrono, turbinadas por operadores funcionais Threading transparente Tratamento](https://reader035.fdocuments.net/reader035/viewer/2022062916/5ec675b5338f896c77290e1a/html5/thumbnails/40.jpg)
public interface SomeView<T> {
Func1<Observable<T>, Subscription> results();
Func1<Observable<Unit>, Subscription> showEmptyState();
Func1<Observable<Unit>, Subscription> hideEmptyState();
Func1<Observable<Unit>, Subscription> showLoading();
Func1<Observable<Unit>, Subscription> hideLoading();
// More delegation }
![Page 41: EVOLUINDO ARQUITETURAS REATIVAS - QConSP · RxJava permite operações assíncronas em um estilo síncrono, turbinadas por operadores funcionais Threading transparente Tratamento](https://reader035.fdocuments.net/reader035/viewer/2022062916/5ec675b5338f896c77290e1a/html5/thumbnails/41.jpg)
public static <T> Subscription bind(Observable<T> observable, Func1<Observable<T>, Subscription> uiFunc) { return uiFunc.call(observable); }
public static <T> Func1<Observable<T>, Subscription> uiFunction(Action1<T> uiAction) { return uiFunction(uiAction, () -> {}); }
public static <T> Func1<Observable<T>, Subscription> uiFunction(Action1<T> uiAction, Action0 done) {
return observable -> observable .observeOn(AndroidSchedulers.mainThread()) .subscribe( uiAction, throwable -> Logger.e(throwable.getMessage()), done ); }
![Page 42: EVOLUINDO ARQUITETURAS REATIVAS - QConSP · RxJava permite operações assíncronas em um estilo síncrono, turbinadas por operadores funcionais Threading transparente Tratamento](https://reader035.fdocuments.net/reader035/viewer/2022062916/5ec675b5338f896c77290e1a/html5/thumbnails/42.jpg)
public static <T> Subscription bind(Observable<T> observable, Func1<Observable<T>, Subscription> uiFunc) { return uiFunc.call(observable); }
public static <T> Func1<Observable<T>, Subscription> uiFunction(Action1<T> uiAction) { return uiFunction(uiAction, () -> {}); }
public static <T> Func1<Observable<T>, Subscription> uiFunction(Action1<T> uiAction, Action0 done) {
return observable -> observable .observeOn(AndroidSchedulers.mainThread()) .subscribe( uiAction, throwable -> Logger.e(throwable.getMessage()), done ); }
![Page 43: EVOLUINDO ARQUITETURAS REATIVAS - QConSP · RxJava permite operações assíncronas em um estilo síncrono, turbinadas por operadores funcionais Threading transparente Tratamento](https://reader035.fdocuments.net/reader035/viewer/2022062916/5ec675b5338f896c77290e1a/html5/thumbnails/43.jpg)
public static <T> Subscription bind(Observable<T> observable, Func1<Observable<T>, Subscription> uiFunc) { return uiFunc.call(observable); }
public static <T> Func1<Observable<T>, Subscription> uiFunction(Action1<T> uiAction) { return uiFunction(uiAction, () -> {}); }
public static <T> Func1<Observable<T>, Subscription> uiFunction(Action1<T> uiAction, Action0 done) {
return observable -> observable .observeOn(AndroidSchedulers.mainThread()) .subscribe( uiAction, throwable -> Logger.e(throwable.getMessage()), done ); }
![Page 44: EVOLUINDO ARQUITETURAS REATIVAS - QConSP · RxJava permite operações assíncronas em um estilo síncrono, turbinadas por operadores funcionais Threading transparente Tratamento](https://reader035.fdocuments.net/reader035/viewer/2022062916/5ec675b5338f896c77290e1a/html5/thumbnails/44.jpg)
public class HelpDeskMessagingActivity extends BaseActivity implements HelpDeskMessagesStreamView {
@Override public Func1<Observable<String>, Subscription> restoreNotSentMessage() { return uiFunction(message -> { Toast.makeText(this, "Erro ao enviar mensagem", LENGTH_SHORT).show(); messageInput.setText(message); }); }
@Override public Func1<Observable<Unit>, Subscription> enableComplaintOption() { return uiFunction(action -> complaintButton.setVisibility(VISIBLE)); }
@Override public Func1<Observable<Unit>, Subscription> disableComplaintOption() { return uiFunction(action -> complaintButton.setVisibility(GONE)); }
@Override public Func1<Observable<Unit>, Subscription> showEmptyState() { return uiFunction(action -> emptyStateContainer.setVisibility(VISIBLE)); }
// More delegate methods
![Page 45: EVOLUINDO ARQUITETURAS REATIVAS - QConSP · RxJava permite operações assíncronas em um estilo síncrono, turbinadas por operadores funcionais Threading transparente Tratamento](https://reader035.fdocuments.net/reader035/viewer/2022062916/5ec675b5338f896c77290e1a/html5/thumbnails/45.jpg)
public class HelpDeskMessagingActivity extends BaseActivity implements HelpDeskMessagesStreamView {
@Override public Func1<Observable<String>, Subscription> restoreNotSentMessage() { return uiFunction(message -> { Toast.makeText(this, "Erro ao enviar mensagem", LENGTH_SHORT).show(); messageInput.setText(message); }); }
@Override public Func1<Observable<Unit>, Subscription> enableComplaintOption() { return uiFunction(action -> complaintButton.setVisibility(VISIBLE)); }
@Override public Func1<Observable<Unit>, Subscription> disableComplaintOption() { return uiFunction(action -> complaintButton.setVisibility(GONE)); }
@Override public Func1<Observable<Unit>, Subscription> showEmptyState() { return uiFunction(action -> emptyStateContainer.setVisibility(VISIBLE)); }
// More delegate methods
![Page 46: EVOLUINDO ARQUITETURAS REATIVAS - QConSP · RxJava permite operações assíncronas em um estilo síncrono, turbinadas por operadores funcionais Threading transparente Tratamento](https://reader035.fdocuments.net/reader035/viewer/2022062916/5ec675b5338f896c77290e1a/html5/thumbnails/46.jpg)
public void userRequiredMediation(String productId, String sellerId) {
MediationParameters parameters = new RequiredMediationParameters.Builder() .productId(productId)
// … .sellerId(sellerId) .build();
executionPipeline(parameters); }
private void executionPipeline(MediationParameters parameters) {
Observable<HelpDeskEventViewModel> execution = source.requireMediation(parameters) .doOnSubscribe(this::prepareToLoad) .map(ViewModelMappers::map) .flatMap(Observable::from) .doOnCompleted(this::finishLoadingMessages);
subscription().add(bind(execution, view().onMessagesLoaded()));
}
PRESENTER LEVEL
![Page 47: EVOLUINDO ARQUITETURAS REATIVAS - QConSP · RxJava permite operações assíncronas em um estilo síncrono, turbinadas por operadores funcionais Threading transparente Tratamento](https://reader035.fdocuments.net/reader035/viewer/2022062916/5ec675b5338f896c77290e1a/html5/thumbnails/47.jpg)
VANTAGENS OBSERVADASPresenter não precisa da noção de threading
Possibilidade de combinação de múltiplas fontes de forma organizada
Presenter passar a orquestrar a UI através de um pipeline de execução bem definido
Tradução de ViewModels é uma etapa do pipeline
![Page 48: EVOLUINDO ARQUITETURAS REATIVAS - QConSP · RxJava permite operações assíncronas em um estilo síncrono, turbinadas por operadores funcionais Threading transparente Tratamento](https://reader035.fdocuments.net/reader035/viewer/2022062916/5ec675b5338f896c77290e1a/html5/thumbnails/48.jpg)
PROBLEMAS OBSERVADOSProtocolo View ainda estava gordo
“Repetição” de código entre Presenters, normalmente relacionada a comportamentos de UI similares
- Mostrar empty state se não houver dados
- Mostrar loading ao iniciar operação; esconder ao terminar
- Controlar interação com Pull-to-refresh
- Estado de erro no caso de problemas, caso não exista conteúdo
- Vários outros
![Page 49: EVOLUINDO ARQUITETURAS REATIVAS - QConSP · RxJava permite operações assíncronas em um estilo síncrono, turbinadas por operadores funcionais Threading transparente Tratamento](https://reader035.fdocuments.net/reader035/viewer/2022062916/5ec675b5338f896c77290e1a/html5/thumbnails/49.jpg)
TERCEIRA INTERAÇÃO
REACTIVE VIEW SEGREGATION
![Page 50: EVOLUINDO ARQUITETURAS REATIVAS - QConSP · RxJava permite operações assíncronas em um estilo síncrono, turbinadas por operadores funcionais Threading transparente Tratamento](https://reader035.fdocuments.net/reader035/viewer/2022062916/5ec675b5338f896c77290e1a/html5/thumbnails/50.jpg)
public interface SomeView<T> {
Func1<Observable<T>, Subscription> results();
Func1<Observable<Unit>, Subscription> showEmptyState();
Func1<Observable<Unit>, Subscription> hideEmptyState();
Func1<Observable<Unit>, Subscription> showLoading();
Func1<Observable<Unit>, Subscription> hideLoading();
Func1<Observable<Unit>, Subscription> networkError();
Func1<Observable<Unit>, Subscription> networkUnavailable();
Func1<Observable<Unit>, Subscription> networkSlow(); }
![Page 51: EVOLUINDO ARQUITETURAS REATIVAS - QConSP · RxJava permite operações assíncronas em um estilo síncrono, turbinadas por operadores funcionais Threading transparente Tratamento](https://reader035.fdocuments.net/reader035/viewer/2022062916/5ec675b5338f896c77290e1a/html5/thumbnails/51.jpg)
UI BEHAVIOR
VIEW PROTOCOL
UI BEHAVIOR UI BEHAVIOR
UI BEHAVIOR . . .
![Page 52: EVOLUINDO ARQUITETURAS REATIVAS - QConSP · RxJava permite operações assíncronas em um estilo síncrono, turbinadas por operadores funcionais Threading transparente Tratamento](https://reader035.fdocuments.net/reader035/viewer/2022062916/5ec675b5338f896c77290e1a/html5/thumbnails/52.jpg)
public interface EmptyStateView<T> {
Func1<Observable<Unit>, Subscription> showEmptyState();
Func1<Observable<Unit>, Subscription> hideEmptyState(); }
public interface LoadingView<T> {
Func1<Observable<Unit>, Subscription> showLoading();
Func1<Observable<Unit>, Subscription> hideLoading(); }
![Page 53: EVOLUINDO ARQUITETURAS REATIVAS - QConSP · RxJava permite operações assíncronas em um estilo síncrono, turbinadas por operadores funcionais Threading transparente Tratamento](https://reader035.fdocuments.net/reader035/viewer/2022062916/5ec675b5338f896c77290e1a/html5/thumbnails/53.jpg)
public interface SomeView<T> extends LoadingView, EmptyStateView, NetworkingReporterView {
Func1<Observable<T>, Subscription> displayResults(); }
public interface NetworkingReporterView<T> {
Func1<Observable<Unit>, Subscription> networkError();
Func1<Observable<Unit>, Subscription> networkUnavailable();
Func1<Observable<Unit>, Subscription> networkSlow(); }
![Page 54: EVOLUINDO ARQUITETURAS REATIVAS - QConSP · RxJava permite operações assíncronas em um estilo síncrono, turbinadas por operadores funcionais Threading transparente Tratamento](https://reader035.fdocuments.net/reader035/viewer/2022062916/5ec675b5338f896c77290e1a/html5/thumbnails/54.jpg)
- Cada comportamento poderia ter o seu “mini-presenter” associado, e o Presenter “grande” faria a orquestração dos colaboradores
- Melhor estratégia : fazer a composição ser uma etapa do pipeline !!!
![Page 55: EVOLUINDO ARQUITETURAS REATIVAS - QConSP · RxJava permite operações assíncronas em um estilo síncrono, turbinadas por operadores funcionais Threading transparente Tratamento](https://reader035.fdocuments.net/reader035/viewer/2022062916/5ec675b5338f896c77290e1a/html5/thumbnails/55.jpg)
f(g(x))
![Page 56: EVOLUINDO ARQUITETURAS REATIVAS - QConSP · RxJava permite operações assíncronas em um estilo síncrono, turbinadas por operadores funcionais Threading transparente Tratamento](https://reader035.fdocuments.net/reader035/viewer/2022062916/5ec675b5338f896c77290e1a/html5/thumbnails/56.jpg)
public class LoadingWhileProcessing<T> implements Observable.Transformer<T, T> {
private PublishSubject<Unit> show, hide = PublishSubject.create();
public Subscription bindLoadingContent(LoadingView view) { CompositeSubscription composite = new CompositeSubscription(); composite.add(bind(show, view.showLoading())); composite.add(bind(hide, view.hideLoading())); return composite; }
@Override public Observable<T> call(Observable<T> upstream) { return upstream .doOnSubscribe(this::showLoading) .doOnTerminate(this::hideLoading); }
private void hideLoading() { hide.onNext(Unit.instance()); }
private void showLoading() { show.onNext(Unit.instance()); } }
![Page 57: EVOLUINDO ARQUITETURAS REATIVAS - QConSP · RxJava permite operações assíncronas em um estilo síncrono, turbinadas por operadores funcionais Threading transparente Tratamento](https://reader035.fdocuments.net/reader035/viewer/2022062916/5ec675b5338f896c77290e1a/html5/thumbnails/57.jpg)
public class OrdersHistoryPresenter extends ReactivePresenter<OrdersHistoryView> {
// Binding all behaviors on view [ ... ]
public void fetchOrders(SearchCriteria criteria) { bind(executionPipeline(criteria), view().displayOrders()); }
private Observable<OrderHistoryType> executionPipeline(SearchCriteria criteria) {
return source.search(criteria) .compose(networkErrorFeedback) .compose(loadingWhenProcessing) .compose(coordinateRefresh) .compose(emptyStateWhenMissingData) .compose(errorWhenProblems) .map(OrdersHistoryViewModelMapper::map); } }
![Page 58: EVOLUINDO ARQUITETURAS REATIVAS - QConSP · RxJava permite operações assíncronas em um estilo síncrono, turbinadas por operadores funcionais Threading transparente Tratamento](https://reader035.fdocuments.net/reader035/viewer/2022062916/5ec675b5338f896c77290e1a/html5/thumbnails/58.jpg)
@Test public void shouldTransformView_RegardlessEmptyStream() {
// When stream has no data Observable<String> stream = Observable.empty();
// and we add transformation to pipeline stream.compose(loadingWhileProcessing) .subscribe( s -> {}, throwable -> {}, () -> {} );
// we should interact with view, anyway verify(view.showLoadingAction).call(uiMethod()); verify(view.hideLoadingAction).call(uiMethod()); }
![Page 59: EVOLUINDO ARQUITETURAS REATIVAS - QConSP · RxJava permite operações assíncronas em um estilo síncrono, turbinadas por operadores funcionais Threading transparente Tratamento](https://reader035.fdocuments.net/reader035/viewer/2022062916/5ec675b5338f896c77290e1a/html5/thumbnails/59.jpg)
VANTAGENSCada evento delegado para a UI agora é unit-testable de uma forma muito fácil !!!
Presenters apenas orquestram a UI (como prega MVP)
Presenter não liga para qual tipo de View está associado
Transformers são facilmente reutilizáveis
![Page 60: EVOLUINDO ARQUITETURAS REATIVAS - QConSP · RxJava permite operações assíncronas em um estilo síncrono, turbinadas por operadores funcionais Threading transparente Tratamento](https://reader035.fdocuments.net/reader035/viewer/2022062916/5ec675b5338f896c77290e1a/html5/thumbnails/60.jpg)
PROBLEMAS ENCONTRADOS (I)1) Boilerplate para o binding de comportamentos
@Override public void bind(OrdersHistoryView view) { super.bind(view); subscription().add(loadingWhileProcessing.bindLoadingContent(view)); subscription().add(networkErrorFeedback.bindNetworkingReporter(view)); subscription().add(coordinateRefresh.bindRefreshableView(view)); subscription().add(emptyStateWhenMissingData.bindEmptyStateView(view)); subscription().add(errorStateWhenProblem.bindErrorStateView(view)); }
✔ Possível solução (WIP) : ViewBinder
![Page 61: EVOLUINDO ARQUITETURAS REATIVAS - QConSP · RxJava permite operações assíncronas em um estilo síncrono, turbinadas por operadores funcionais Threading transparente Tratamento](https://reader035.fdocuments.net/reader035/viewer/2022062916/5ec675b5338f896c77290e1a/html5/thumbnails/61.jpg)
PROBLEMAS ENCONTRADOS (II)2) Comportamentos injetados via DI no Presenter; possível confusão ao fazer pull das dependências
✔ Possível solução (WIP) : ViewBinder fornecido via DI + configuração definida na apresentação
3) Cooperação entre comportamentos ✔ Possível solução (WIP) : Transformers agregadores
![Page 62: EVOLUINDO ARQUITETURAS REATIVAS - QConSP · RxJava permite operações assíncronas em um estilo síncrono, turbinadas por operadores funcionais Threading transparente Tratamento](https://reader035.fdocuments.net/reader035/viewer/2022062916/5ec675b5338f896c77290e1a/html5/thumbnails/62.jpg)
CONCLUSÕES
![Page 63: EVOLUINDO ARQUITETURAS REATIVAS - QConSP · RxJava permite operações assíncronas em um estilo síncrono, turbinadas por operadores funcionais Threading transparente Tratamento](https://reader035.fdocuments.net/reader035/viewer/2022062916/5ec675b5338f896c77290e1a/html5/thumbnails/63.jpg)
LIÇÕES APRENDIDASEscolher um modelo de arquitetura não é uma tarefa trivial
Evoluir um modelo para obter vantagens de um paradigma (FRP) é ainda menos trivial
Não tenha medo de errar; adote iterações na evolução da arquitetura
Esforço se paga no médio prazo
![Page 64: EVOLUINDO ARQUITETURAS REATIVAS - QConSP · RxJava permite operações assíncronas em um estilo síncrono, turbinadas por operadores funcionais Threading transparente Tratamento](https://reader035.fdocuments.net/reader035/viewer/2022062916/5ec675b5338f896c77290e1a/html5/thumbnails/64.jpg)
https://speakerdeck.com/ubiratansoares/evoluindo-arquiteturas-reativas
![Page 65: EVOLUINDO ARQUITETURAS REATIVAS - QConSP · RxJava permite operações assíncronas em um estilo síncrono, turbinadas por operadores funcionais Threading transparente Tratamento](https://reader035.fdocuments.net/reader035/viewer/2022062916/5ec675b5338f896c77290e1a/html5/thumbnails/65.jpg)
OBRIGADO@ubiratanfsoares
ubiratansoares.github.io
https://br.linkedin.com/in/ubiratanfsoares