A neat diagram comparing Meteor, Flux, and Relay

Nobody doubts the functional approach that Elm et al are promoting works, and may very well be the way of the future. Kudos for being an early adopter–the Meteor community definitely needs to become more adventurous like yourself.

That said, you’re missing the point of my article: to honor what functional programming gets right and start a conversation about how to bring the best parts of it to imperative object oriented programming.

…Just stating that “explicitness” is the way to go–not to be rude, but that’s basically like what everyone in 2015 with access to a blog and a little React experience has been doing–is not adding anything to the conversation. Though, in your favor, I do agree us Meteor developers have been blind way too long (me included), especially in comparison to the greater NPM community. 2015 was especially fast, so let it serve as a wakeup call to us.

But let’s move to the next step and start thinking how we can improve Meteor. What you–as in you specifically, Chet–want is a functional stack top to bottom based on observables (imagine cycle.js on the client all the way down to the database). Very similar to what the Datomic database is bringing to the Clojurescript world (which I know you’re aware of, as its integration is a to do item in your “elmish” package):

I.e. similar to what’s described in “Turning the Database inside out”, popularized by Redux creator, Dan Abramov:

…That’s a different framework altogether–and if Meteor was ever to become that there wouldn’t be a single line of code from their current platform. I’m speaking hyperbolically but you get the idea. My point is either way: I agree with you–this would be awesome! Let’s go start a company and build that. Until then, while we’re still using Meteor, let’s figure out what we can fix in a fraction of the time and get it done. Multiple approaches work–so rather than trash one, let’s realize the precise strengths and weaknesses and get to work where we can add value. The main strength being that Meteor is built already and the main weakness of the impending functional stack is that it’s currently just hypothetical. Parts of it are done, particularly the client side, but everything below that HAS NOT ARRIVED YET. I’m considering going being part of that future myself, i.e. its arrival, i.e. helping it to arrive sooner, i.e. building it! That said, I think an incremental approach improving our current tools needs to happen regardless. If you watched @staltz’s video at Reactive Conference:

you will see that we are making improvements to our imperative “horse” while the new functional stack is, what seems to be, a “BMW” lol. …But one hidden takeaway I got from that video is that people will have a hard time migrating from the OOP imperative approach to functional for years to come. The more we can make the OOP imperative approach like the functional approach (like React has done with hybrid functional class-based components), the more we can bridge that gap, the easier we can make it for people to transition to our brighter functional future. I’m not even saying I’m going to be the one to do it and carry the imperative flag for much longer–I’m this close to jumping ship completely like @staltz did with Cycle.js. Either way I have compassion for the struggles with predictability that imperative OOP programmers have and will continue to have, and understand–that for business reasons, the time it takes to learn or other, even plain old stubbornness–it’s not an option for everyone to just jump ship to functional programming. I would also argue it’s not the right time either. The industry is in the midst of so much upheavel. I wouldn’t even build an app right now, if I had the choice. I’d just be part of creating the foundation of the future, and that’s basically what I’m doing with these Medium articles and things like TrackerReact.

The problem explicit fanboys have with implicit abstractions is that so many implicit abstractions are leaky and not complete. If they were rock solid, we wouldn’t be having this conversation. Would you write 100 lines instead of 20 lines if 20 lines was 100% guaranteed to achieve what you want without problems?? No, you wouldn’t. What I’m saying today is Tracker has been far from “rock solid,” but if it was rock solid you wouldn’t even have the trace errors you’re talking about.

In short, we aren’t talking about “magically changing the meaning of [our] code”–we are talking about getting right what Tracker got wrong all along. Tracker never should re-run your code if you don’t even use the fields in your find call! That’s not the expected behavior. It’s nonsense we have put up with because the ease of Meteor application development has lulled so many of us into thinking we could never build the internals of what Meteor is doing ourselves–meanwhile the greater NPM community and sharp individuals like yourself are discovering every day that’s not true and there is a life outside of Meteor [patented @SkinnyGeek1010 concept :)].

You are correct about potentially missing state, but for the wrong reasons. Minimongo is a subset of what’s in Mongo–by caching/snapshotting all of Mongo on your local machine, you guarantee not only that you capture minimongo, but also subscriptions and everything behaves as normally; you guarantee that stale server side data doesn’t come back, which is most important. …and I’m actually incorrect as well–there are local collections too–so we have to snapshot both minimongo and all of mongo, but it’s still not that big of a deal since you only need one extra snapshot, and then a log of all mutations to bring it up to speed when necessary.

But I’m not done disproving your hypothesis that we “[cannot] guarantee [we’ll] reproduce” state well enough to be useable :

…Now, if in Redux you used any immutable data, any global state, within your reducers, you would f#*! it up. right. So similarly in Meteor, any state outside of Minimongo–because of the fact that it’s not tracked–would break things. It doesn’t have to do with the state being held outside the [client] application in Mongo on the server as you’re pin-pointing the problem to be. It has to do with making sure ReactiveDict, Session (which is ReactiveDict), and all the most common reactive datasources are snapshotted/cached in the same way. AND THEN on top of that, like reducers in Redux, relies on you not using global variables or properties on global variables to store some extra state. Again, Redux has the same problem; you’re just instructed not to do do that–we can provide similar instructions.

The fact of the matter is most Meteor apps do a good job of relying on reactive datasources rather than plain old global state anyway–so we are most of the way there. They need them to do the reactive stuff Meteor is all about. So it’s not that big of an “ask” to ask developers to just use reactive data sources if they count on time traveling working. If they want to add a new reactive data source, e.g. @luisherranz 's ReactiveState, well then there’s a simple API he can use to partake in the snapshotting goodness.

But I agree, there are holes. But I would say easy holes to keep plugged if you want to partake in Time Traveling and [accurate] hot module reload. I think a “more than good enough” solution for facilitating time traveling and HMR is completely possible, and in fact easy to build. The more challenging part in fact is traversing the dependency graph and providing dispose, accept etc handlers like Webpack so that HMR occurs properly–so for the purpose of this conversation, let’s limit it to Time Traveling.

Time traveling [yes, in a mutable environment] is easily achievable by snapshotting et al as discussed.


I’ll check out Elmish. That’s dope that you used Ramda to make it.

4 Likes

I don´t use it at the moment, but sift.js may help to query with mongo selectors.

2 Likes

I’m confused by what you mean. Are you just talking about a special edge case of what happens when a new version of your app is sent to the client?

I don’t get what exactly you mean by hot code push and why it makes you pull your hair out! Sorry maybe I’m not experienced enough meteor dev :smile:

I agree @ccorcos, I never liked Tracker for the reason that is magical and (is non-deterministic the correct term?).

However, I still haven’t found a SIMPLE way to build a meteor app aside from using getMeteorData (I’m using react, not blaze). I like Meteor for its simplicity, and I feel like Redux and Flux requires me to drink some kool-aid that I’m not really interested, until I see a good reason for it!

I’m still waiting to to see a GOOD, NON-DOGMATIC example of how I can use Flux/Redux with Meteor and React, when it is actually is needed.

With meteor, a hot code push is a new version of the application that is sent to the client.
You have various ways to persist session data (not commited in db) so that the connected client doesn’t loose the data he is creating/editing.
The usual way is to use “Session” (not recommended) or a ReactiveDict, which both achieve the same thing.
But then you have to consolidate three sources of data in a typical CRUD application: data from the db, session data, and (previous) session data before the last hot code push.
I guess that most meteor dev just drop hot code push persisted data, because that make it easier to develop your app, but i find it more elegant to allow your users data to persist between hcp, but that is not easy to code.

Yeah, once we have ES6 modules, those examples will come out.

1 Like

Hm, that’s an interesting problem I didn’t think much about. It seems like if you’re pushing a new version of client in a production app it would be safest to prompt the user, make sure all data is saved, and then refresh the page, don’t you think?

I never thought of hot code push as a good feature in a production app, I saw it as a convenience during development.

That’s the way Google is doing it, that’s probably a good way, and i think i remember there is a package doing that.

Even for development, if you app is not preserving session data, there is no use for this feature, imho.

Not necessarily, but I hear you, it’s nice to preserve state if possible. I guess I don’t think about it much, I usually treat my app as stateless, I realize I hardly ever store anything in the session, I rarely put things into React state, mostly I write everything to DB asap (since Meteor makes it so easy)… or I try to encode state in the URL for example if I have a page with many sections.

What about an Meteor app architecture with the following rules:

  • All app state must be stored in in MongoDB (Like the single store with Redux)
  • All state mutations are triggered through Meteor Method calls (Like actions in Flux / Redux)
  • Views get their data from Minimongo Collections (by subscribing to data and then using cursors) or the parent view.

I would like to know examples where this wouldn’t work for you or other thoughts you have on this.

More in detail thoughts on the points:

  • All app state must be stored in in MongoDB
    • Only one place to store state
    • We get the full benefits of Meteor: Data synchronisation between server and clients and persistence of data.
  • All state mutations are triggered through Meteor Method calls
    • A Meteor Method call is the same as using an action creator.
    • We get action/event transfer to the server + simultaneous handling on the client for latency compensation
    • The current design of the API let you think that you can only have one handler for a Method call, but you can have multiple by just imperatively calling multiple functions or introducing a dispatcher that works on the client and server. The support for multiple Meteor method implementations could be something that can be built in in a future version of Meteor core.
    • Changing your MongoDB state in Meteor method handlers will synchronize the state between server and client automatically the right way (= built in latency compensation feature of Meteor).
  • Views get their data from Minimongo Collections or the parent view.
    • In Flux / Redux views also get their data from the store(s). In this architecture the Minimongo Collections are the stores.
    • You can also think of the set of all Minimongo Collections as the one store, where each collection manages a specific part of the store. As plain object this looks like this: {docs: [...], comments: [...], attachments: [...]} where docs, comments and attachments are Minimongo collections.
2 Likes

All app state must be stored in in MongoDB (Like the single store with Redux)
All state mutations are triggered through Meteor Method calls (Like actions in Flux / Redux)
Views get their data from Minimongo Collections (by subscribing to data and then using cursors) or the parent view.

Was actually implementing this for a project, but with redux as the client store. One big advantage of redux is the addons: thunk, devtools…

What about having the option of using redux instead of minimongo? each collection that you would store in minimongo would simply be in the store, and upon update (using dispatch), it would update the server as well.

Sanjo exactly, but I’ve taken this approach in Redux itself.

Domain State comes from Mongo via Subscriptions or Method Calls(dumping in local collections)

UI State comes from a Redux Store which is a Reactive Dict.

When domain state changes, The Store dispatches an async action which calls the Meteor method for the state change.

Then everything comes full circle.

I’ve been there. And that’s when I realised Flux/Redux doesn’t fit in Meteor as they are. They are defined for a React-only world without transparent reactive programming (Tracker).

I tried it. I tried to dispatch an action each time Meteor “did something magical” and make everything Action-driven like Flux/Redux. But Meteor is not Action-driver, it is Data-driven. So what happened is that I started adding a lot of boilerplate here and there. I had to manually keep the Redux-like store in sync with Minimongo or any other reactive source out there.

Finally, I decided to add reactivity back to my Redux-like store and all that pain and boilerplate disappeared. You just do:

State.modify('posts.items', (state = []) => {
  return Posts.find().fetch();
});

and reactivity keeps things simple again.

But still, you got flexibility to define it depending on stuff happening on your app:

State.modify('posts.items', (state = []) => {
  switch(Action.type()) {
    case 'INIT':
    case 'CATEGORY_UNSELECTED':
      return Posts.find().fetch();
    case 'CATEGORY_SELECTED':
      return Posts.find({ category: Action.category }).fetch();
    default:
      return state;
  }
});
1 Like

hey @luisherraz good to read you. What do you thinks of mobservable? … how fit it on Meteor? … is overlap with MeteorFlux?
I make a post about: Mobservable and Meteor 1.2.1 is possible?

Still trying to choose an archicture. At the end MeteorFlux is the only develop for Meteor.

Mobservable is a great package. It’s transparent reactive programming (yeah, Tracker) for the React world and it shows the advantages of TRP in terms of code which is simple to reason about and less prone to edge case bugs and even performance gains when used right (check this https://www.mendix.com/tech-blog/making-react-reactive-pursuit-high-performing-easily-maintainable-react-apps/ out).

The main differences I see between Mobservable and Tracker are two:

  • Mobservable has all state inside and Tracker has state outside.

  • Mobservable is sync and Tracker is async.

Both are related. Sync stuff is easier to debug than async stuff.

I tried to make my ReactiveState package sync because when you own all state, you can execute stuff synchronously:

State.modify('tasks.areEmpty', (state = true) => {
  if (State.get('tasks.items').length === 0)
    return true;
  else
    return false;
});

State.modify('tasks.items', (state = []) => {
  return Tasks.find().fetch();
});

When you are evaluating tasks.areEmpty you can stop at the State.get('tasks.items') and execute its modify first, make sure it is up-to-date and then continue with tasks.areEmpty. Functions are executed synchronously and only once. Perfect!

The problem with that is, once again: Meteor reactivity comes from many places, not only a single tree. For example, add another reactive source to the mix: Meteor.userId().

State.modify('user.hasPendingTasks', (state = false) => {
  if (Meteor.userId() && State.get('user.tasks').length === 0)
    return true;
  else
    return false;
});

State.modify('user.tasks', (state = []) => {
  let userId = Meteor.userId();
  return Tasks.find({ userId }).fetch();
});

If you want to make this work synchronously, you need to know if Meteor.userId() has already changed or is going to change before executing any of the State modifiers.

The sad truth is: you cannot know. Tracker reactivity has no priorities and is spread in many places along Meteor. So the solution is what Meteor people already did: make Tracker async and wait for every reactive source to invalidate its computations. Then start executing computations.

For that reason, I don’t think Mobservable is useful or has any advantage over Tracker in Meteor if you don’t want to throw a lot of stuff which is already working with Tracker.

For Tracker, this means sometimes functions are going to be executed twice or even more times until they reach an stable state. That’s the reason MeteorFlux actions have three phases:

  1. Register callbacks (sync non-reactive).
  2. State.modifies or Tracker.autoruns (async reactive, it keeps executing functions until everything is stable).
  3. AfterAction callbacks (sync after all reactivity has finished and is stable, non-reactive).

Explained like this, this looks like really complex stuff, but in the end coding with TRP is simpler and less prone to bugs. Most of the time you don’t have to know about how it works internally and that’s what makes Tracker and Meteor so simple to start with.

@luisherranz One think that I don´t undestand.
You calll MeteorFlux TRP but it follow the Flux pattern that imply imperative call actions froms component´s, say React. On the other side, with mobserver, you declare what ‘data’ is observable and what React componentes observe this data. To me this look ‘decoupled’/transparent reactive.

Sorry for the confusion. MeteorFlux has three phases, two of them sync and non-reactive (Flux actions) and another in the middle using async TRP (State modifiers, using Tracker).

By the way, I’ve just read this article from @mweststrate: https://medium.com/@mweststrate/becoming-fully-reactive-an-in-depth-explanation-of-mobservable-55995262a254#.1m59bm20f

It’s implementation of chain execution is very interesting and similar to what I tried to achieve with ReactiveState. But to solve the unstable situations it introduces a concept called transactions which is very interesting and would solve the example I gave in my last post.

1 Like

That’s why im only using redux for UI state. I’m treating any domain state update as a “side effect”. With thunk you can dispatch functions to handle them, works pretty nicely.

1 Like

How do you manage when Minimongo gets updated automatically by Meteor due to changes in other client Minimongos?

All meteor related updates minimongo included are domain state.

If you’re writing methods with tons of side effects you’re def gonna see different reactions to changes.

UI state is like visibility filters, active states, random stuff you’re already doing with reactive vars.

Is that what you were asking?

1 Like