
Workday Prism Analytics: The Search for a Strongly-Typed, Immutable State

Workday Prism Analytics is an advanced data analytics platform that is built on top of the Workday software system. It allows organizations to analyze data from a variety of sources and gain insights into their business operations.
Workday Prism Analytics provides users with a range of tools and features to help them analyze and visualize their data, including:
- Data Integration: Workday Prism Analytics can integrate data from a wide range of sources, including Workday and non-Workday systems, into a single analytics platform. This enables organizations to gain a comprehensive view of their business operations.
- Data Transformation: Workday Prism Analytics also provides tools to transform and prepare data for analysis. Users can clean, normalize, and enrich their data to ensure that it is accurate and complete.
- Data Modeling: Workday Prism Analytics allows users to create custom data models to support their specific analysis needs. This includes the ability to define relationships between different data sources and create hierarchies and calculations.
- Data Visualization: Workday Prism Analytics provides a range of data visualization tools to help users create interactive dashboards and reports. Users can create charts, graphs, and tables to visualize their data and gain insights into their business operations.
- Collaboration: Workday Prism Analytics also allows users to collaborate on data analysis projects. Users can share reports and dashboards with colleagues, and collaborate on data modeling and analysis tasks.
Workday Prism Analytics has undergone a series of transformations as it has evolved. Today, our Workday Prism Analytics front-end is set to undergo another exciting shift towards a more modular architecture.
Enter React. We chose React from the plethora of JavaScript platforms to simplify development through its standardized framework and to maximize performance through its virtual DOM, which updates the real DOM only for necessary change-sets. Most importantly, React’s component-based architecture enables microservices throughout Workday to contribute to and consume from a centralized library of reusable components.
In regards to a view model, we wanted a single source of truth for state transparency and update clarity, so Redux seemed like a fitting state-management pattern. Here, components dispatch actions — objects composed of a type and payload — that are processed by pure functions known as reducers. The reducers in turn update the store, or state, of the application. We then use Reselect selectors to read necessary properties from our store in a modular fashion, hiding all state access from the components themselves.
Finally, for the developmental advantages of a strongly-typed system, and to keep consistent with the rest of the Workday ecosystem, we chose the JavaScript superset TypeScript as our base language, posing the following question: how can we implement an immutable Redux store while adhering to TypeScript’s strongly-typed syntax?
Plain Objects
The most lightweight option, using native JavaScript objects to maintain Redux state, requires no additional API knowledge or external dependencies. Here, to enforce an immutable store, each update must copy state to a new object, rather than merely mutating the original state.
With this approach, there is unfortunately no way, other than developer due diligence, to guarantee absolute immutability. Accidental state mutations can slip through reviews undetected and cause complications. Complex linters to detect state assignment can help ameliorate some woes here, but none are perfect. Finally, with complex state, nested updates likely will result in multiple levels of copying and can quickly escalate into slow and unmaintainable operations.
Immutable.js
Immutable, a library developed and maintained by Facebook, has received a lot of praise for its performant and powerful immutability framework. The library works by mapping native JavaScript objects and arrays to custom, immutable data structures called Maps and Lists, respectively. Implemented through hash array-mapped tries, these structures have guaranteed immutability and performant updates.
At first glance, Immutable seems like an extremely promising option, but can it conform to a strongly-typed language? Let’s dive a little deeper.
First, Immutable adds a layer of abstraction with its custom structures, meaning developers need to learn a new, slightly verbose API for setting and getting properties. Additionally, since we wish to create reusable components that other teams can consume, we need to avoid introducing external dependencies in the components themselves. Therefore, Immutable must be restricted to our reducers and selectors.
To avoid dependencies, we must convert our JavaScript state to and from Maps and Lists. Immutable provides two convenient methods, fromJS and toJS, which do just that, albeit with a price. Neither method preserves objects’ type signatures, and both have quite the performance impact (see the performance chart below), two detrimental pitfalls.
Immutable.js with Records
We can ameliorate several of these issues by using another Map-like object introduced by Immutable: Record. With a bit of overhead, we can implement custom Records with explicit types represented in our state, allowing us to avoid using from JS in state initialization.
Now, our state not only has all the benefits Immutable offers, but it is also strongly typed, so methods such as set, get, and merge can realize TypeScript’s static type-checking.
Unfortunately, we still run into issues here with nested structures; neither setIn nor getIn, used to update and get nested properties, preserves types. Using either of these extremely useful methods again sacrifices our beneficial type-checking, and implementing a combination of get, set, and mergeDeep, if possible, can quickly become unmaintainable. Furthermore, Records do not solve our problem with toJS’s performance when mapping state properties to components in our selectors.
Seamless-Immutable
Next, a library similar to Immutable on the surface, Seamless-Immutable introduces a subtly different type of immutable structure. Seamless’ structures, unlike Immutable, are backwards-compatible with JavaScript objects and arrays, effectively eliminating any need to convert objects; the added dependency here should have no effect on our functional components. Hence, Seamless solves our Immutable performance issue of complex object conversion.
This library has some detrimental flaws, however, that prevent us from seriously considering it for Workday Prism Analytics’ use case. First, when trying to update or read nested state properties, Seamless runs into the same problem of type-loss that we saw with Immutable Records. Second, as of this writing, Microsoft Internet Explorer 9 (IE) and later doesn’t support this library in its entirety. Workday supports IE 9 and later, and maintaining a separate immutability framework solely for IE seems extremely impractical.
Immer
Exploring another option, Immer is a lightweight library written by Michel Weststrate (creator of the Redux alternative MobX), which works by using proxies and copy-on-write. Given an initial state, it produces a proxied draft that we can freely modify and ultimately returns a new object, leaving our initial state unchanged. As a result, Immer guarantees immutability. Additionally, due to its use of structural sharing and proxies, Immer processes updates as performantly as Immutable.
Immer offers other benefits. For one, it has pure API simplicity. Immer consists of one, straightforward function (demonstrated below) that is easily consumable. Most importantly, since we are dealing with native JavaScript objects and arrays, property types are preserved throughout, regardless of nesting level. Finally, our selectors need not worry about object conversion or its accompanying performance impact.
This library comes with its drawbacks as well. IE11 does not support proxies, meaning Immer needs to rely on its significantly slower es5 fallback, a potentially significant issue for complex state updates. Fortunately, Immer’s simplicity makes it completely opt-in on a reducer by reducer basis, so native objects — or another alternative — could be selectively used if need be.
Second, Immer is still in its infancy. Michel Weststrate published Immer roughly three months ago (as of this writing), and it does not have the backing support of a large company like Immutable does via Facebook. Issues, should they arise, have the potential of going unaddressed.
Immutable application store, but these are not by any means the only ones available. In fact, Redux Ecosystems alone lists over 65 packages to help with immutable data structures. Of these, immutability-helper and immutable-assign also stood out as other viable options, the latter seeming similar to, albeit lesser known than, Immer.
Given this vast multitude of frameworks and helper packages, it would be impossible to examine each one in detail. We considered certain libraries because of their prevalence in the front-end community, simplicity, reliability, and potential for TypeScript compatibility. Other frameworks may better suit different needs, especially if there is no desired support for TypeScript or IE.
We’ve seen, strongly-typed immutability has quite a few possible solutions, each with its own benefits and downsides. With the prevalence of so many intersecting technologies, hopefully this article helps to simplify your choices in building a modern, scalable front-end application.