Central Data Store (WIP)

Today, we do not have a central data store. All of our MFEs have been built without such a capability, and so we currently have no active use cases for it in our current functionality. This is by way of saying that any usage of a central data store will be net new product or platform features, and we haven’t gathered requirements yet on what those could be.

The short answer could be, simply, “no, we don’t need a central data store.” At least at first.

That said, for the purposes of this document, we’ll imagine some possible use cases, and will take for granted that having inter-MFE communication and data sharing will be an enabling capability that will open up possibilities for new extensions and innovations.

There are a few kinds of data sharing we can envision:

Configuration

Shared Config

Today, each MFE loads its own configuration document (either through build-time or runtime config). Much of the config values in these are the same across MFEs, and it’d make sense for them to be defined and made available in one centralized place owned by the shell application.

MFE-specific Config

Some configuration is MFE-specific, and we can imagine that either stays local to the MFE’s code, or gets added to the global configuration document namespaced by the MFE’s unique name. It feels unlikely that other MFEs would need to access it, so I’d likely suggest it stays private to the MFE that owns it.

Global State

The utility of global state partially depends on where we draw the boundaries between MFEs, the shape of each MFE’s data at rest in the client, and how we want to handle client-side cache invalidation of data. MFEs in the same domain are more likely to share information. Account settings and profile, for instance, share detailed user preference data on the client. Course home and courseware should share course structure data, don’t, but are currently in the same MFE and likely shouldn’t.

If a user is switching back and forth between course home and courseware, for instance, or modifying their account settings and checking out the changes in their profile, we could drastically improve performance by caching this data in a global state store for some period of time (a few minutes?)

We’ll also need to build in facilities for evicting data from global state when a user has navigated away beyond the cache expiration.

Implementation

As far as an implementation, I expect the right answer is to use redux, since many MFEs (including those examples shown above) use it for storing data they receive from the server already, our community is familiar with it, and it’s a best in class tool used throughout the industry.

Redux is not a singleton, and multiple stores can co-exist on the page, which would allow MFEs to keep some data ‘private’ as desired, while sharing data we expect to be common with the central data store.

If we want child MFEs to be able to dynamically add reducers to a global redux data store, we’ll need to provide a mechanism to do that, which we don’t have today. There are documented approaches to doing this.

Alternatives

It’s worth noting that API caches (like the LocalforageCache in frontend-platform/auth) play an important role in improving performance here too - given that we make use of BFF (backend for frontend) endpoints in some cases, the API cache would have a miss even though some of the entities are the same.

In many cases, this is going to be advanced performance optimization, which can be more of an art than science, and should likely be performed based on user activity metrics which we may not have today.

Eventing

It’s difficult to imagine use cases today where we’ll need an eventing system to publish and subscribe to messages. Many things that may seem like events may be better suited to an imperative API exposed by the shell, or through frontend-platform.

For instance, one thing that comes to mind is an MFE telling the shell to switch to a different header or footer. The advantage of eventing is that we don’t need to know or care who the event’s consumers are; in this case, we very much want to tell a specific entity (the header) to change. That’s not an ideal use case for events.

Put another way, we should be careful to use events for sharing information, not making requests.

i18n Messages

As MFEs load, they need to get their localized messages into the internationalization system so they’re available to be used. We can imagine that dynamically loaded components may export their messages alongside the component, and the shell will pass those on to the i18n system as they come through. Alternately, the component could handle adding them to the i18n system itself when it renders.