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

Janet. Build command-based architecture in reactive manner.

Janet. Build command-based architecture in reactive manner.

Alexandr Kozlov

July 16, 2016
Tweet

Other Decks in Programming

Transcript

  1. Usual Service Layer • Communication with server side • Processing

    business layer • Working with entities (storage) • Security rules • Other use cases
  2. 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
  3. 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 +
  4. Usual Service Layer • Communication with server side • Processing

    business layer • Working with entities (storage) • Security rules • Other use cases } Actions
  5. When we think about actions NetworkManager UserManager StorageManager LoginAction RegisterAction

    UsersAction AddFriendAction DeleteFriendAction StoreFriendsAction
  6. Problem of different approaches • async-http • Goro • EventBus

    • DBFlow • Retrofit 2 • Goro • RxJava • Realm • Retrofit • RoboSpice • RxJava • SnappyDB
  7. Based on actions Flexibility and scalability. 
 Scale functionality using

    services Reactive approach for actions
 interaction by RXJava Throw-safety architecture We made Janet
  8. Janet Flow Result Action Send ActionPipe Subscribe Janet Service Result

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

    ActionState PROGRESS SUCCESS FAIL START ActionState = Action + Status
  10. Services Janet janet = new Janet.Builder()
 .addService(new HttpActionService(API_URL, httpClient, converter))

    
 .addService(new SqlActionService(...))
 .addService(new XyzActionService(...))
 .build();
  11. 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;
  12. 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
  13. 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
  14. How to use? // Create pipe for action class with

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

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

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

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

    janet ActionPipe<SampleHttpAction> 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();
  19. How to use? @Override
 protected void onResume() {
 super.onResume();
 usersPipe.observeWithReplay()


    .compose(bindToLifecycle())
 .observeOn(AndroidSchedulers.mainThread())
 .subscribe(new ActionStateSubscriber<UsersAction>()
 .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());
 }
  20. 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<ActionState<UserAction>> subscriber = new TestSubscriber<>();
 apiInteractor.userActionPipe()
 .observe()
 .subscribe(subscriber);
 apiInteractor.userActionPipe().send(new UserAction());
 assertActionSuccess(subscriber, action -> action.getUser().equals(testUser));
 }

  21. 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.
  22. ActionServiceWrapper public class AuthServiceWrapper extends ActionServiceWrapper {
 
 private final

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

  23. Command • Custom operations • Business use cases • Janet

    action chains Processed by CommandActionService See https://github.com/techery/janet-command
  24. Command @CommandAction
 public class SaveBitmapCommand extends Command<File> {
 
 private

    final Bitmap bitmap;
 
 public SaveBitmapCommand(Bitmap bitmap) {
 this.bitmap = bitmap;
 }
 
 @Override
 protected void run(CommandCallback<File> 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();
 }
 }
  25. Command @CommandAction
 public class UploadBitmapCommand extends Command<UploadResult> {
 
 @Inject


    Janet janet; private final Bitmap bitmap;
 
 public UploadBitmapCommand(Bitmap bitmap) {
 this.bitmap = bitmap;
 }
 
 @Override
 protected void run(CommandCallback<UploadResult> 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);
 }
 }
  26. 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