spotify / mobius

A functional reactive framework for managing state evolution and side-effects.
https://spotify.github.io/mobius/
Apache License 2.0
1.22k stars 97 forks source link

Clarification on my learnings on mobius loop #157

Closed rajdheepak closed 1 year ago

rajdheepak commented 2 years ago
  1. Is mobius-core and mobius-test compatible with KMM ? - I can see that both these modules are completely platform agnostic, assuming this is right theoretically i believe it is possible to use these in KMM modules CMIIW
rajdheepak commented 2 years ago
  1. Can a single screen have multiple loops like can a viewmodel hold multiple loops controller? Do you have any experience/insights on this ?

Context around asking this question:

rajdheepak commented 2 years ago

Suggestions apart from above questions:

wdygt?

pettermahlen commented 2 years ago
  1. No, Mobius isn't compatible with KMM. It could be (and ideally should be), but we haven't had the time to invest in that. The non-platform-agnostic parts of mobius-core that make it harder is of course everything related to concurrency.
  2. There is nothing that stops you from having multiple loops for a single UI screen. Usually, if you have too many effects (or events), that can be a signal that you might be able to improve your design. But otherwise, since effect handlers are easily split up (branching per effect), it's generally not hard to work with domains even if they have a large number of effects. The logic for each effect tends to be simple and manageable.
  3. I don't understand the last suggestion, maybe you can clarify with an example in code of how you'd like to write it?
rajdheepak commented 2 years ago

@pettermahlen Thanks for the answers i think 2 answers all of my questions.

rajdheepak commented 2 years ago

A Follow up question that we had while analysing if mobius loops would fit in for us:

pettermahlen commented 2 years ago

I don't understand the question I'm afraid - 'middleware' can mean many things, and so can 'hooks'. Could you explain what your use case is, and what you're trying to achieve?

rajdheepak commented 2 years ago

@pettermahlen I mean the middleware from the redux one.

More specifically, Middleware in Android development, using Redux approach, is a program layer for asynchronous tasks, their managing, scheduling and subscribing. we can probably use it for API requests, logs, analytics, persistence, other time-consuming operations that you want to execute on a separate thread (to free-up the UI Thread, for example).

pettermahlen commented 2 years ago

In Mobius, you would typically do that using EffectHandlers. Mobius is different from Redux, for example in how it separates Events from Effects, whereas Redux just uses Actions. So if the loop decides something should happen in the outside world, it will dispatch an Effect, which an EffectHandler can decide to handle asynchronously.

rajdheepak commented 2 years ago

@pettermahlen Thanks for the answers, a few more questions we would like to clarify before starting to use mobius loops:

  1. Why should update functions be only pure can I not switch thread and make a request and fire some event that will update the state?

  2. When I have to make parallel requests, I dispatch a set of effects do the events come in clubbed as a result of all of them or will it be separate event actions getting fired? Or is there a better way of doing things around making parallel requests?

  3. If I want to make sequential requests, I can do this in a single effect but its like putting in domain logic into effect handlers. So should we fire an effect which returns an event which in turn fires the effect the for the next request to be made? Or is there a better way of doing this?

pettermahlen commented 2 years ago
  1. See https://spotify.github.io/mobius/objectives/#pure-functions for a description - no, making the Update function impure is in direct contradiction to using Mobius at all. :)
  2. https://spotify.github.io/mobius/objectives/#concurrency-and-ordering has a description of some of these concerns. Mobius doesn't proscribe a specific way to handle parallel requests, but it does give you a lot of tools to handle it safely and correctly.
  3. The two ways to sequence side effects in Mobius are the ones you describe. Either Effect -> Event -> Effect chains (which are cumbersome, but safe), or an Effect Handler that is stateful and ensure that Effect 1 finishes before Effect 2 is executed.
rajdheepak commented 2 years ago

@pettermahlen Thanks for the answers, we are from Gojek evaluating architectures and have kinda finalised on using mobius loop core package.

A few more questions coming your way, hope i am not nudging you too much 😉

  1. Lets assume we want to perform heavy computations like on an dataset of huge array, since this being pure function would you still do this on update or in an effect handler? - Slightly a follow up on the first question that we already had asked, Can we do all of the update pure functions on a different thread?

  2. We were thinking of having different loops for each component on the UI screen, have seen a few threads where @anawara suggests using event sources for communication across loops. - Any guidelines or learnings from the spotify context of this?

pettermahlen commented 2 years ago
  1. This depends on how you've configured your event runner. There are of course situations when you might block the main thread, or some other important thread, which you might not want.
  2. I don't have anything general to say - it's a question of tradeoffs. Using effect handlers is nice because it hides the fact that Mobius is used on both sides (so you can change that). The opposite is to integrate everything into a single loop (and compose that out of sub-loops, like in https://www.pointfree.co/collections/composable-architecture). That is nice because it's less plumbing, but it is less suitable for larger apps because it creates closer ties between different parts.
rajdheepak commented 2 years ago

@pettermahlen Whats the default thread for update and effect functions? i can see here two work runners are created but don't really get what thread they would be running on by default. Does this mean both update and effect functions don't happen on the main thread by default?

` return new Builder<>(

    update,
    effectHandler,
    null,
    (Connectable<M, E>) NOOP_EVENT_SOURCE,
    (MobiusLoop.Logger<M, E, F>) NOOP_LOGGER,
    new Producer<WorkRunner>() {
      @Nonnull
      @Override
      public WorkRunner get() {
        return WorkRunners.from(Executors.newSingleThreadExecutor(Builder.THREAD_FACTORY));
      }
    },
    new Producer<WorkRunner>() {
      @Nonnull
      @Override
      public WorkRunner get() {
        return WorkRunners.from(Executors.newCachedThreadPool(Builder.THREAD_FACTORY));
      }
    });

`

pettermahlen commented 2 years ago

Yes, that's what it means.

The Executors class is documented here, in case you need more information about how the different methods work.

CyborgRahil commented 2 years ago

@pettermahlen

We have a use case where we need to convert an observable stream to event source.

Checking the API documentation:

In RxMobius we can convert an Observable stream to an event source using fromObservables(). Doc link: https://www.javadoc.io/doc/com.spotify.mobius/mobius-rx2/latest/com/spotify/mobius/rx2/RxEventSources.html

Did not get any luck with Mobius core . Is there any API in Mobius core?

What is the recommended approach to achieve this using mobius-core?

togi commented 2 years ago

mobius-core does not have any dependencies on RxJava at all by design - if you need rx utilities then you have to include the appropriate mobius-rx/rx2/rx3 library in your project and use the RxEventSources class

rajdheepak commented 2 years ago

Thanks for the previous answers @pettermahlen

Are permission accepted/denied considered External events?

Scenario: Lets take a use case where we want to be making location pings every 5 minutes, for this we will need to ensure location permission is given.

Approach-1: Get permission state in Effect handler by asking platform layer and ask user if not already given (here the access given/denied comes back as a callback to effect handler itself.)

Approach-2: Effect handler asks platform layer to check for permission and this comes back in as a external/user interaction event to the loop.

Which one do you see going with the style of Mobius without breaking principles?

pettermahlen commented 2 years ago

Both are fine, it's a matter of taste.

rajdheepak commented 1 year ago

Hey @pettermahlen we are going ahead with using Mobius architecture for our application.

We were thinking of using coroutines flow. What are your thoughts on the flowbius from trello?

https://github.com/atlassian-labs/Flowbius

pettermahlen commented 1 year ago

Glad to hear you're moving on with Mobius, I hope it will be useful to you!

I haven't used Flowbius myself, so cannot make a recommendation either way. I think it looks nice though.

I will close this issue as it seems your questions have been answered.

rajdheepak commented 1 year ago

@pettermahlen We are mostly using mobius loops with viewmodel across our app.

What is the use case for a mobius controller?

Can Mobius Controller be thought of an equivalent to VM with loop?

Could you give me actual example use cases for Using Mobius controller over VM with loop?