Slide 1

Slide 1 text

Janet Build command-based architecture in reactive manner Alexander Kozlov Android software engineer

Slide 2

Slide 2 text

What’s our application made of?

Slide 3

Slide 3 text

What’s our application made of? User Interface

Slide 4

Slide 4 text

What’s our application made of? User Interface UI Presentation

Slide 5

Slide 5 text

What’s our application made of? User Interface UI Presentation Service Presentation

Slide 6

Slide 6 text

What’s our application made of? User Interface UI Presentation Service Presentation Entities Service

Slide 7

Slide 7 text

Usual Service Layer • Communication with server side • Processing business layer • Working with entities (storage) • Security rules • Other use cases

Slide 8

Slide 8 text

Usual Service Layer • Communication with server side • Processing business layer • Working with entities (storage) • Security rules • Other use cases ➡ NetworkManager ➡ UserManager, ChatManager ➡ StorageManager ➡ SecurityManager ➡ (*)Manager

Slide 9

Slide 9 text

Usual Service Layer • Communication with server side • Processing business layer • Working with entities (storage) • Security rules • Other use cases ➡ NetworkManager ➡ UserManager, ChatManager ➡ StorageManager ➡ SecurityManager ➡ (*)Manager Use Cases, Repositories, Adapters Interactors +

Slide 10

Slide 10 text

Usual Service Layer • Communication with server side • Processing business layer • Working with entities (storage) • Security rules • Other use cases } Actions

Slide 11

Slide 11 text

When we think about actions NetworkManager UserManager StorageManager LoginAction RegisterAction UsersAction AddFriendAction DeleteFriendAction StoreFriendsAction

Slide 12

Slide 12 text

Encapsulation Reusability Scalability Inheritance What we think about actions Declarative Mockable Testable

Slide 13

Slide 13 text

Problem of different approaches • async-http • Goro • EventBus • DBFlow • Retrofit 2 • Goro • RxJava • Realm • Retrofit • RoboSpice • RxJava • SnappyDB

Slide 14

Slide 14 text

Problem of different approaches • Action-based Framework • Action-based Framework • Action-based Framework

Slide 15

Slide 15 text

Based on actions Flexibility and scalability. 
 Scale functionality using services Reactive approach for actions
 interaction by RXJava Throw-safety architecture We made Janet

Slide 16

Slide 16 text

Janet Structure Janet ActionPipe Service Presentation ActionPipe ActionPipe Service

Slide 17

Slide 17 text

Janet Flow Action

Slide 18

Slide 18 text

Janet Flow Action Send ActionPipe

Slide 19

Slide 19 text

Janet Flow Action Send ActionPipe Janet

Slide 20

Slide 20 text

Janet Flow Action Send ActionPipe Janet Service

Slide 21

Slide 21 text

Janet Flow Action Send ActionPipe Janet Service Result

Slide 22

Slide 22 text

Janet Flow Action Send ActionPipe Janet Service Result Result

Slide 23

Slide 23 text

Janet Flow Result Action Send ActionPipe Subscribe Janet Service Result Result PROGRESS SUCCESS FAIL START

Slide 24

Slide 24 text

Janet Flow Result Action Send ActionPipe Subscribe Janet Service Result Result PROGRESS SUCCESS FAIL START Result = Action + Status

Slide 25

Slide 25 text

Janet Flow ActionState Action Send ActionPipe Subscribe Janet Services ActionState ActionState PROGRESS SUCCESS FAIL START ActionState = Action + Status

Slide 26

Slide 26 text

Services Janet janet = new Janet.Builder()
 .addService(new HttpActionService(API_URL, httpClient, converter)) 
 .addService(new SqlActionService(...))
 .addService(new XyzActionService(...))
 .build();

Slide 27

Slide 27 text

Services To add new ActionService you should override 3 methods • getSupportedAnnotationType - defines what actions are processed by their class annotation; • sendInternal – is called upon new action is sent to pipe; • cancel – is called upon action is canceled from pipe;

Slide 28

Slide 28 text

Possible Services LocationActionService PersistActionService SSHActionService BitmapActionService FTPActionService TrackingActionService SyncActionService GITActionService RESTActionService VideoActionService SQLActionService

Slide 29

Slide 29 text

Janet Services • HttpActionService to provide HTTP/HTTPS requests execution • AsyncActionService to provide support for async protocols, e.g. socket.io • CommandActionService to delegate job back to command action

Slide 30

Slide 30 text

Janet Action @HttpAction(value = “/demo/{id}”, method = GET) 
 public class SampleHttpAction {
 @Query("param") String param; @Path("id") String id;
 @Response SampleData responseData;
 } Processed by HttpActionService
 See github.com/techery/janet-http

Slide 31

Slide 31 text

How to use? // Create pipe for action class with janet ActionPipe actionPipe = janet.createPipe(SampleHttpAction.class);

Slide 32

Slide 32 text

How to use? // Create pipe for action class with janet ActionPipe actionPipe = janet.createPipe(SampleHttpAction.class); // Register request result observer
 actionPipe.observe() .subscribe(new ActionStateSubscriber()
 .onStart(action -> {})
 .onProgress((action, progress) -> {})
 .onSuccess(action -> {})
 .onFail((action, throwable) -> {})
 );

Slide 33

Slide 33 text

How to use? // Create pipe for action class with janet ActionPipe actionPipe = janet.createPipe(SampleHttpAction.class); // Register request result observer
 actionPipe.observe() .subscribe(new ActionStateSubscriber()
 .onStart(action -> {})
 .onProgress((action, progress) -> {})
 .onSuccess(action -> {})
 .onFail((action, throwable) -> {})
 ); // Send action actionPipe.send(new SampleHttpAction()); // or actionPipe.createObservable(new SampleHttpAction()).subscribe();

Slide 34

Slide 34 text

How to use? // Create pipe for action class with janet ActionPipe actionPipe = janet.createPipe(SampleHttpAction.class); // Register request result observer
 actionPipe.observeWithReplay() .subscribe(new ActionStateSubscriber()
 .onStart(action -> {})
 .onProgress((action, progress) -> {})
 .onSuccess(action -> {})
 .onFail((action, throwable) -> {})
 ); // Send action actionPipe.send(new SampleHttpAction()); // or actionPipe.createObservable(new SampleHttpAction()).subscribe();

Slide 35

Slide 35 text

How to use? // Create pipe for action class with janet ActionPipe actionPipe = janet.createPipe(SampleHttpAction.class); // Register request result observer
 actionPipe.observeSuccessWithReplay() .subscribe((action) -> doSomething(action)); // Send action actionPipe.send(new SampleHttpAction()); // or actionPipe.createObservable(new SampleHttpAction()).subscribe();

Slide 36

Slide 36 text

How to use? @Override
 protected void onResume() {
 super.onResume();
 usersPipe.observeWithReplay()
 .compose(bindToLifecycle())
 .observeOn(AndroidSchedulers.mainThread())
 .subscribe(new ActionStateSubscriber()
 .onStart((action) -> showProgressLoading(true))
 .onSuccess(action -> {
 updateData(action.getResponse());
 showProgressLoading(false);
 })
 .onFail((action, throwable) -> showProgressLoading(false)));
 }
 
 @OnClick(R.id.button)
 public void loadUsers(){
 usersPipe.send(new UsersAction());
 }

Slide 37

Slide 37 text

How to test? @Before
 public void setup() throws JanetException {
 MockHttpActionService httpActionService = new MockHttpActionService.Builder()
 .bind(new MockHttpActionService.Response(200)
 .body(testUser), request -> request.getUrl().endsWith("user"))
 .build();
 Janet janet = new Janet.Builder().addService(httpActionService).build();
 apiInteractor = new ApiInteractor(janet);
 }
 
 @Test
 public void loadUsers() {
 TestSubscriber> subscriber = new TestSubscriber<>();
 apiInteractor.userActionPipe()
 .observe()
 .subscribe(subscriber);
 apiInteractor.userActionPipe().send(new UserAction());
 assertActionSuccess(subscriber, action -> action.getUser().equals(testUser));
 }


Slide 38

Slide 38 text

ActionServiceWrapper Abilities: • Pre/post processing • Additional business logic • Intercepting, re-routing Decorator for ActionService is used to listen for action status or add additional intercepting logic. Possible solutions: caching middleware, Dagger injector, retry policy, etc.

Slide 39

Slide 39 text

ActionServiceWrapper public class AuthServiceWrapper extends ActionServiceWrapper {
 
 private final Storage storage;
 
 public AuthServiceWrapper(HttpActionService actionService, PreferenceWrapper prefs) {
 super(actionService);
 this.storage = prefs;
 }
 
 @Override protected boolean onInterceptSend(ActionHolder holder) {
 if (holder.action() instanceof BaseHttpAction) {
 String authToken = storage.getToken();
 if (!TextUtils.isEmpty(authToken)) {
 ((BaseHttpAction) holder.action()).setToken(authToken);
 }
 }
 return false;
 }


Slide 40

Slide 40 text

Command • Custom operations • Business use cases • Janet action chains Processed by CommandActionService See https://github.com/techery/janet-command

Slide 41

Slide 41 text

Command @CommandAction
 public class SaveBitmapCommand extends Command {
 
 private final Bitmap bitmap;
 
 public SaveBitmapCommand(Bitmap bitmap) {
 this.bitmap = bitmap;
 }
 
 @Override
 protected void run(CommandCallback callback) throws Throwable {
 File file = new File(........);
 FileOutputStream stream = new FileOutputStream(file);
 try {
 boolean saved = bitmap.compress(Bitmap.CompressFormat.JPEG, 90, stream);
 if (saved) {
 callback.onSuccess(file);
 } else {
 callback.onFail(new IllegalStateException("Compressing error"));
 }
 } finally {
 stream.close();
 }
 }

Slide 42

Slide 42 text

Command ActionPipe actionPipe = janet.createPipe(SaveBitmapCommand.class); actionPipe.createObservable(new SaveBitmapCommand(bitmap))
 .subscribe(new ActionStateSubscriber()
 .onSuccess(command -> {
 File file = command.getResult();
 doSometing(file);
 })
 .onFail((command, throwable) -> showError(throwable)));

Slide 43

Slide 43 text

Command @CommandAction
 public class UploadBitmapCommand extends Command {
 
 @Inject
 Janet janet; private final Bitmap bitmap;
 
 public UploadBitmapCommand(Bitmap bitmap) {
 this.bitmap = bitmap;
 }
 
 @Override
 protected void run(CommandCallback callback) throws Throwable { janet.createPipe(SaveBitmapCommand.class)
 .createObservableResult(new SaveBitmapCommand(bitmap))
 .map(SaveBitmapCommand::getResult) .flatMap((file) -> janet.createPipe(UploadHttpAction.class) .createObservableResult(new UploadHttpAction(file)) .map(UploadHttpAction::getResponse) )
 .subscribe(callback::onSuccess, callback::onFail);
 }
 }

Slide 44

Slide 44 text

Janet Service Layer Interactors ActionPipe ActionPipe ActionPipe Services Wrappers Presentation UI

Slide 45

Slide 45 text

Janet https://github.com/techery/janet Contributions are welcome Version: Used in 4 project in 3 different componies Tested on production

Slide 46

Slide 46 text

Janet Samples: • Simple Android app: https://github.com/techery/janet-http-android-sample • Advanced Android app: https://github.com/techery/janet-architecture-sample • Flux-like Android app: https://github.com/techery/janet-flux-todo

Slide 47

Slide 47 text

We are hiring! techery.io/jobs