sialcasa / mvvmFX

an Application Framework for implementing the MVVM Pattern with JavaFX
Apache License 2.0
494 stars 105 forks source link

Idea: Flux/Redux based state #474

Open mainrs opened 7 years ago

mainrs commented 7 years ago

Some people and I are currently writing an application using JavaFX. We started to discuss about using a redux like singleton state within the whole application. But we came across some issues quite fast.

For example, using a MVVM based application architecture alongside Redux would eliminate JavaFX property bindings. An immutable state does not allow the manipulation of JavaFX properties (if I am not wrong). i had the idea of binding the View's properties to the ViewModel's properties and notifying the ViewModel through some callback interface that the state of the application changed. The ViewModel could then set the new values for its properties and the UI could update.

Have you considered something similar for mvvmFx? Have you had the same problems as we have? And did you find any solution to it? Edit: This coudl (theoretically) be used to eliminate scopes and the underlying problem of ScopeProviders.

bekwam commented 7 years ago

Does your MVVM / Redux design collapse the View Model (Screen State) and the Model (Record State) into one Store?

mainrs commented 7 years ago

If I understood that correctly, my current approach would be to store the model within the store. If data changes, the ViewModel dispatches an Action and therefore manipulating the state. If the state gets changed, each ViewModel that subscribed to it would get notified about a change and would have the chance to update its properties. Looks like I just store the Model state within the store.

manuel-mauky commented 7 years ago

Hi. Actually I've implemented a flux "framework" (it's more a prove-of-concept though) here: https://github.com/lestard/FluxFX and also gave a talk about it at last years JavaOne converence. Sadly, it wasn't recorded. If you understand german you can find a recording of (almost) the same talk that I gave at a german converence here: https://vimeo.com/167728524

There is also a really interesting Redux library for JavaFX here: https://github.com/netopyr/reduxfx

My opinion is: I really like redux for JavaScript apps together with React.js but I'm not fully convinced yet that Java as a language is a good fit for it. And also JavaFX has some characteristics that doesn't seem to fit with the idea of redux. It's heavily depending on "state" and OOP while redux is based on functional programming and immutable data. But if you are interested in redux you should definitly take a look at reduxfx.

Flux on the other hand is way more compatible with JavaFX in my opinion and while it's core ideas are completely different to the ones of the MVVM pattern (MVVM is all about two-way binding while Flux only uses one-way-binding) it's quite easy to implement a Flux architecture with mvvmFX framework: Just make sure that all your ViewModels only provide ReadOnly-Properties and create "actions" for all mutating operations. These actions can be send as notifications via the notificationCenter and all ViewModels then can listen for action types they are interested in. It's not "real" Flux architecture but very similar.

mainrs commented 7 years ago

Ye, I understand german :D I will take a look. I was thinking about alternative ways to provide data to my application. I started to work with React and Redux and liked the idea. I took a look at the second library too. But in my opinion, the API is a bit messy. Did you heard of Mobx? Another way of providing data to your application via a global state. Its approach is OOP, that might fit better within the JavaFX ecosystem.

manuel-mauky commented 7 years ago

Yes I've read about MobX but if I have the possibility to use functional programming, why would I still choose OOP? ;-) For that reason I've never really gave it a try.

bekwam commented 7 years ago

"MVVM mit JavaFX" on YouTube is also a good resource, even for viewers who don't speak German.

mainrs commented 7 years ago

@lestard What about the standard MVVM pattern but instead of using scopes to provide data, the ViewModel keeps track of changes within the stores it needs to operate. And the ViewModel can dispatch actions to the store. That would make it still easy to test code without the need of a JavaFX runtime. The View would still bind its properties to the ViewModel ones. Would this be an approachable idea?

manuel-mauky commented 7 years ago

@SirWindfield "... Would this be an approachable idea?" There is only one way to find out ;-)

As first approach you could define "ViewModel=Store". The ViewModel acts as a Store and no separate store is used. In the original Flux pattern there are multiple Stores possible (in contrast to redux) so this would work. However, the scope (here I don't mean the mvvmFX scope) of the ViewModel and the Store in Flux is different. A Flux store encapsulates a part of a domain while a ViewModel encapsulates only the data of one view. There can be multiple parts of the UI connected to the same flux store while typically only one view is connected to a single viewModel. In a typical app the number of ViewModels will be much bigger then the number of flux stores. They don't really match each other. So "ViewModel = Store" actually isn't true. They aren't the same.

The second approach could be to say "mvvmFX Scope = Store". At first sign this looks much better because multiple ViewModels can be connected to a single scope. However, the range in which a scope instance is valid is based on the View structure which isn't true for Flux stores. So Scopes aren't a good match for Stores either.

The third approach would be to think of Flux Stores as "Model" in terms of MVVM. In this approach your ViewModel would bind itself to the read-only properties of the Stores and send actions to a dispatcher while the Stores are listening to the actions. I think this could work. However, I don't have time to play around with this and implement an example. And it's not the scope of mvvmFX to support Flux architecture so we may not add additional features just for this use case. If it's possible to implement Flux with existing features I would document it in the official docs though.

manuel-mauky commented 7 years ago

Hi everyone, I have two updates to those who are interested regarding this topic:

  1. I've played around a little bit more with Redux and JavaFX and implemented an example of how you can use redux with FXML here. The goal is to add this API into the reduxfx project which is already discussed here. We try to decouple the core reduxfx-API from the View part. This would also enable us to use ReduxFX together with mvvmFX. ReduxFX would take up the "Model" part while the ViewModel still exists to decouple low-level UI-Logic from FXML.
  2. At Saxonia Systems AG we have an interesting customer project in the pipeline that will use mvvmFX in a similar way with exactly the same ideas in mind. In this project we can't use the ReduxFX library but instead another propritary redux-like framework. Of cource I can't post internals of customer projects but I will definitly try to come up with similar OpenSource examples.

Mymain point is: This topic is hot and we are working on stuff like this. And it's likely to see improvements or new features for mvvmFX to support this kind of redux/flux like applications in the future.

rguillens commented 6 years ago

Regarding global state managenent, I've been searching, reading, thinking and trying to achieve something similar to Vue.js and Vuex (a Redux like library) global state management in JavaFX.

The main idea is simple: create a global store (like Vuex) in java (I already have a very simple and minimalistic implementation based on RxJava), and use it in mvvmFX views (just like Vue.js). View and ViewModel use two way data-binding. The store can be used to dispatch sync/async registered "Actions" and "Mutations" and registered "Getters" are exposed as RxJava Observables so you can subscribe to them anywhere.

What do you think about this approach?

manuel-mauky commented 6 years ago

Hi, this sounds promising. I've implemented a similar approach in ReduxFX. There is a sub-module to support FXML Views in ReduxFX. You can see it in action here.

Basically, it allows you to access two components: "Dispatcher" and "Selector". The Dispatcher can be used to dispatch new Actions and the Selector is used to get notified on state-changes from the redux core. In the example I'm not using MVVM but instead are binding the values directly to FXML controls but of cause you could decouple this by using mvvmFX. I would love to provide such a mvvmFX-reduxFX-combination example but due to limited time I haven't been able to get this done.

I don't know the specifics of Vue.js and Vuex but as far as I know this would be quite similar. If you create an example of your approach I would be highly interested. I suppose that we don't need any changes in mvvmFX to support this approach but I'd love to link to examples in our wiki. I'm a bit sceptical to introduce deticated features/modules to mvvmFX for this but if it really turns out to be valuable and useful I can imagine to add such features to the mvvmFX framework. However, I would love to not reimplement the wheel but instead use and integrate in existing projects like ReduxFX or to just provide a small Integration-API that is agnostic to specific implementations.

Another side-note: In https://github.com/sialcasa/mvvmFX/issues/474#issuecomment-299425469 I've already mentioned a customer project with similar concepts in mind. In the meantime we have successfully finished the first part of this project and the redux-like implementation of global logic and state handling with local mvvmFX support works quite well.