Time not important : Temporal Transparency in RxJS
Free your Angular/React code from the burden of “when”
In most Single Page Applications, the functions responsible for querying the server simply return the data from the HTTP responses. However, when spaced in time, identical requests can have different results, forcing developers to deal with different representations for the same entity.
To prevent this issue, when writing a function that returns Observables, derive them from a reactive Single Source of Truth.
The Full Story
In the past year, I have been working on several Single Page Applications, mostly with Angular 2 and React. All those applications were written in TypeScript, and as quite a few other people, I have experimented with different approaches in client-side state management. All my experiments involved SPAs and RxJS, however this article describes a principle that can be applied to any stack where a UI and a reactive library are involved.
As I experimented and studied the work of others, I have seen signs of an emerging pattern, and I believe it deserves a name.
This article assumes a basic understanding of RxJS. The code examples are written in TypeScript. If you are more of a Babel person, just ignore the type annotations.
Let’s start with a formal, obscure definition. You might want to come back to it once you have read the rest of the article :
Temporal Transparency is a property of functions that return Observables. Such a function is said to be temporally transparent if for the same arguments, the returned Observables are eventually equivalent, meaning that whenever one of them emits a new value, all of them do. All those Observables can be described as converging towards a Single Source of Truth.
The caller of a temporally transparent function does not care about when it makes the call. For the same arguments, the returned Observables are equivalent.
Enough of this nonsense ! Show me the code !
First, let me give you an example of a function that is NOT temporally transparent :
If you have any experience with RxJS, you should feel outraged by this code. It is absolutely terrible. The
getCount()method returns an Observable, but that Observable will never emit more than a single value, which kind of undermines the point of returning an Observable in the first place. If you call it repeatedly, you’ll end up with many different Observables, each representing a snapshot of the counter state at a specific time.
Now let’s make this
getCount() method temporally transparent, with as few changes as possible :
Obviously, they are better ways to implement this, but we achieved our goal. Without changing the public API of the
counter object, we managed to make the
getCount() method temporally transparent. And it feels so much better !
With this change, all the returned Observables will emit a new value every time the counter is incremented. It no longer returns a snapshot of a fixed value at a specific time. Instead, it returns a representation of a value that changes over time. And that representation is our Single Source of Truth.
You no longer have to care about when you call the method. In practice, you have made time not important.
Time not important.
Real life example
OK, the previous example was quite obvious. But hang on, things are going to get interesting :
Does this code feel wrong in any way ? Probably not. I’m sure you have already written similar code. I certainly have.
But if you compare it to our first “bad” example, you’ll realize the returned Observables exhibit the same issues :
- they will never emit more than one value
- they represent a snapshot of the server state at a specific time
What happens if I call the method twice with the same id as argument, but in the meantime the data has changed on the server ? We’d have to deal with multiple versions of the same entity. Strange behavior might occur. Some component could display details of an entity, right next to another component presenting an older, outdated version of that same entity.
We accept the problem because we have learned to live, and found solutions to deal with it. The whole point of this article is the claim that we should eliminate it at the root.
peopleData.fetch()method is NOT temporally transparent. So let’s make a few changes : (please don’t judge the implementation details, I aimed for simplicity so everyone can figure out what’s going on)
peopleData.fetch() will still trigger an HTTP call to the backend, that part doesn’t change. But here we don’t simply return the response body. We return the entity’s unique, reactive representation. Our reactive Single Source of Truth,
peopleById$, makes sure only one
Person instance can be referenced for a single id. And whenever a new value is fetched from the server, every single Observable ever returned for that id will update. They will all reflect the truth, eventually.
Time not important.
Only truth important.
Going further: CQS (Command-Query Separation)
Something feels wrong in the code above. We were trying not to break the API, but in doing so we wrote a
fetch() method which binds together two separate actions: updating and reading the state. Let us separate them :
Whether or not you might want to enforce this separation or not, or something in between, is enough material for another article by itself. Here I’m just trying to get you started on that line of thought.
This article aims to start a discussion about a principle, not so much about implementation details. However, here’s a couple ideas worth mentioning.
If you aim to capture all the data flowing from the server and model it as a Single Source of Truth, it needs to be normalized. Data duplication has to be eliminated to prevent the storing different versions of the same entity. Using something like normalizr should help, although not required.
I recently ran into this very promising project, RxDB. From what I have seen, one could store in it all the data received from the server, then use it as reactive Single Source of Truth for the whole client application.
Promises VS Observables
Some developers insist on returning Observables everywhere they could have returned a Promise. I don’t necessarily agree. I like to think of Observables as values that changes over time, whereas Promises represent future outcomes of asynchronous operations. Returning an Observable that never emits more than an single value is often a violation of the Temporal Transparency Principle, and I always contemplate the possibility to return a Promise instead. Also, it communicates my intent better. When I return an Observable, you can reliably expect it to emit multiple values.
Think about it next time you write a function that returns an Observable. Ask yourself: Is it Temporally Transparent ? Is the returned Observable derived from a Single Source of Truth ? Can it emit more than one value ? Could I find myself in the situation where I have to handle multiple representations of the same entity ?
Keep in mind, I’m not saying that all your functions should be Temporally Transparent. All I’m saying is you should ask yourself the question. Most programming principles often need to be violated, but when you do, make sure it is for a reason.