Real store’is: Utility stores (part 1 of 3)

MobX stores definitions in a real-world app

José Manuel Aguirre
ITNEXT

--

This post show the “State Management” side of timefic.com, which means we are not talking here about React components, rendering into the dom or handling user events. It will be about how to organize data (state) and how to model any app based in 3 kinds or families of stores:

  • Utility stores: Stores that are “architectural” to the app (and probably the app you are building now) so they can be used by all the modules or services defined in your “big picture”.
  • Local stores: Stores that are persisted locally or not persisted at all. They hold temporary data but are critical for the client-side experience of your users.
  • Domain stores: Stores that are related to a specific domain or service and where the business rules have its home.

So, we will not be seeing in this post much code, maybe some images for a visual representation of the ideas and because I know how much you love to see javascript code 😀 !!

Store #1: The Root Store

AKA: The mother of all stores

When you create your first stores, you progressively put there all the functionality you need, for example:

  • Add fields (observables) that most store will need like dbData (data coming from your server) and ready (a boolean that indicates this data is already loaded).
  • Make the call to the server to populate the data you need.
  • Create a generic action called modify to change the value of any field (observable) you declare.
  • Log each store to the window object, for example: window.ActionsStore.
  • Initialize the store with data instead of loading the data from your server (useful for example when you are testing the system).

And in my case also to complete hide MobX primitives from any store that will be constructed using this Root Store: observable, action, computed, reaction, when, autorun to mention the most frequently primitives used. If you want to know more about this pattern check out this article.

A fragment of the code used to define the Root Store

Store #2: The Checks Store

AKA: The doorman.

Do you need to validate an email or password? A telephone number or a minimum length of a string when your user types inside an input box?

In my case I went through 3 stages to solve this problem:

  • Validate at the action level: As the user types (onChange event) or when he submits the field (onClick event), an action is dispatched. The action takes this input but before going to the backend checks if the email looks good or not. This is fine until you need to validate an email in another place: are you going to write the same code again?
  • Validate using a utility function: No, you know you will not be able to live with this evident code duplication (and you are aware also there will be a triplication in the future…). So you create utility functions named checkEmail, checkPassword, checkToken, etc. This is good, no duplication here, but you need to store the result of this validation somewhere don’t you?
  • Validate using a store: Our friend the store appears. It will not only hold the utility functions you wrote (it’s nice for a function to have a home) but will also store the data that is returned from the checks you do. So, a React component can be observing this data and show a nice message (also defined in the store!) to the user. Finally, because this error should not be visible forever, you will define also a standard mechanism to make it disappear: a timeout for example.

Store #3: The Alerts Store

AKA: The usability enhancer.

Similar story than the Checks Store here.

Suppose a user just created a contact, sent an invitation to a meeting or made a contribution note to the secretary.

The second after submitting this actions he will be asking himself: Did I send it? Did he receive it? After Whatsapp invented the single and double check you know users are expecting immediate feedback for certain actions (not every action please!).

So, this store provides a standard mechanism to create observable data that will be shown by a React component, using standard messages (defined also at the store level) and with a mechanism for dismissing the alert (a timeout or an explicit handler to remove it).

Remember this are utility stores, so you will need to decide how they are used inside a concrete module of your app.

For example: in timefic I made the choice to have all Checks Stores being an exact copy of the utility store because I decided its use is “universal”.

But for the Alerts Store I decided that every module will extend the utility store with its own alerts definitions.

Example of an Alert defined in the Dashboard module

Store #4: The Queue Store

AKA: The superintendent.

This store is in charge of orchestrating all the actions that come from the user, then go to the backend (once or more than once) and then come back to the user.

The orchestration has a “fancy” promise-based implementation that is the result of the need for combining synchronous actions with asynchronous ones.

For example:

  • The organizer of the meeting creates a new agreement to be voted, calling an action with the field text (the description of the agreement).
  • The State (a store we will be looking in part 2 of this article) already knows the meeting_id that is currently being used. So, the action creator does not need to explicitly inform this value.
  • We call a method on the backend called AddAgreement passing this values and get back the _id of the agreement once created.
  • This _id allows to update the State to have this new agreement now selected in the screen.
  • Finally, we clean the State (the text of the new Agreement) to be available for future new agreements.
An action in the form required by the Queue Store

The Queue Store mission is to receive actions like this from all over the places and put them on an internal Queue (an observable array of our store).

Each action is processed in a sequential manner and each step inside each action also is. Because each step can be synchronous or asynchronous, what we do here is wrap each step inside a Promise.

That's the way the queue controls the flow of execution an that’s why client and server can pass data to each other after each step is done.

The best of this approach? I have hundreds of actions defined like this and the pattern still remains valid.

And also, the action definition it’s just an object. Take a look: isn’t it cute?

Store #5: The Local Store

AKA: The little db.

This store is just a wrapper for the LocalStorage of your browser.

The main reason for this store to exist is to be able to be included as a step for the Queue Store. For example, when the user sets a preference for language ‘Spanish’ you can change the state, go to the backend and also save it locally in your browser. Each action, one step for the queue.

Also here you can polyfill the local Storage (in case you are in a browser that doesn’t support localStorage you can use cookies) and also use it as a wrapper for the AsyncStorage that React Native use.

Not much more here. This store is just an interface to communicate with the data the user has saved locally with convenient ways of getting, setting, clearing, counting and checking for availability.

A definition for the Local Store

Store #6: The Clocks Store

AKA: The synchronizer.

This store is not so generic as the previous store are, but I made it a utility store because it’s used in many of the “app modules” of timefic (Dashboard, Planner, Meeting).

Time management is key inside timefic, so this may be also your case, depending on the app you are building.

Warning: This store is not “the result of years of development”… It’s just an approach that works for me so use it with care 😊

In a nutshell, this store:

  • Has an observable field named serverClock.
  • Every 1000 milliseconds, it checks if there is a new value from serverClockFromStream.
  • If yes, the serverClock is updated with this value.
  • Otherwise, then the serverClock is updated adding 1000 ms to the previous value.

This way, from the backend every 10 seconds (you define the interval that best fit your needs) the timestamp is sent to all connected clients (field serverClockFromStream).

This store is imported into the modules that need time management and extended with computed fields base on the serverClock and other fields. For example: the elapsedTime of the meeting? Subtract startDate from serverClock.

Note: Take a look also at mobx now, an observable field that has the time every second. This may work for you also. It’s a simple way to have different fragments of you app in sync (but not with your server).

Store #7: The Streams Store

AKA: Serverless communication between clients

Finally, the Streams Store is another pattern that appeared in timefic when I was trying to solve:

  • Inform the other people connected to the chat when I was typing (and receiving same information also).
  • Show my cursor inside the slide that I am currently presenting.
Serverless communication: The organizer shows something to the audience

Of course, you need a React component to subscribe to this event and make your magic with CSS.

But, who acts as the server if this is serverless? To be clear, this is not strictly serverless but it’s not using your server, which means the data comes from another server. As an example, I use PubNub service to emit and subscribe to this events.

In the next episode, we will be talking about Local Stores or stores that don’t persist its data to a backend and are used to encapsulates complexity of the user interface.

--

--