Last month, I did some noodling on Message Queues; in particular, why Message Queues don't carry the stigma of an "integration database" despite the fact that they are a database (of sorts) being shared by disparate services. As InVision App undergoes its metamorphosis, I like to partake in this kind of thought-experiment because integration databases have caused me much pain in the past. Historically, I've only thought about integration databases as a back-end concern. Yesterday, however, as I was talking to Bryan Stanley - one of our architecture leads - it occurred to me that the Browser has data-stores on the front-end that may be worth considering. Specifically, the localStorage API and the Cookies collection.
The driving force behind the anti-pattern of an "integration database" is the matter of ownership. With an integration database, a service exercises direct access to data that is owned by a different service. This direct access makes the system very brittle because changes in the core service often create unexpected breaks in the parasitic service.
| || || |
| || |
| || || |
When discussing an integration database, it's easy to get lost in what "ownership" actually means. Especially when the disparate services are all part of a single product or platform that's owned by a single company or business unit. So, for the purposes of this conversation, I define the "ownership" boundary as a "build and deployment" process. In the above diagram, Service A and Service B are built and deployed separately (possibly by different teams, possibly by the same team). As such, any data store that is directly accessed by both services is, by definition, an integration database.
NOTE: I find the "build and deployment" definition especially helpful as we move into the world of FaaS (Functions as a Service). If two Functions are built and deployed separately, then any database that they both Functions read-and-write to is, by definition, an integration database.
Now that we undrestand how to define our boundaries, let's turn our focus from the server-side to the client-side. In many applications, the entire front-end is managed by a single build and deployment process. As such, in those applications, there is no question as to whether it is OK for different aspects of that front-end to access data stores like localStorage and Cookies (whether that access be explicit or implicit). But, as an application grows in size and complexity, it's possible that the front-end is broken up into different payloads that are built and deployed separately.
Consider something like a JWT (JSON Web Token); or, an XSRF (Cross-Site Request Forgery) token; or, a NONCE (one time) form token. It's not uncommon for such values to be stored in the Browser Cookies. But, what happens when one distinctly deployable unit sets these values and then another distinctly deployable unit consumes those values:
| || || |
| || |
| || || |
You don't have to squint very hard for this front-end diagram to look like the previous back-end diagram. And, just as with the back-end relationship, this may create a brittle front-end relationship. For example, what happens if "Deployable Unit A" wants to rename the Cookie in which it stores the JWT or XSRF token? It's not that difficult to imagine logic that would require a re-build and re-deploy of "Deployable Unit B" in order to prevent that name change from creating a breaking change in the overall application.
What makes Cookies an even more interesting type of data store is that they are often accessed implicitly, not explicitly. Meaning, they are automatically slurped up into outgoing HTTP requests being made by the browser. But, this behavior is based on the settings of the Cookie itself. Which opens up a whole new set of "ownership" questions.
Imagine, if you will, that "Deployable Unit A" creates a cookie that uses the domain:
DOMAIN = .mycompany.com
The leading "." indicates that this cookie applies to "mycompany.com" as well as "*.mycompany.com" subdomains. This may be used because "Deployable Unit A" makes calls to multiple back-ends and wants to pass that cookie value along with all HTTP requests.
Now, imagine that the business requirements of "Deployable Unit A" change and it no longer needs to communicate with multiple back-end services. In order to increase security, it locks the cookie down to the single domain that it communicates with:
DOMAIN = www.mycompany.com
Due to the way Cookies work, this DOMAIN change may have a direct impact on "Deployable Unit B" and "Deployable Unit C", which may no longer slurp this cookie into their outgoing HTTP requests. As such, we may find ourselves in a situation in which one deployment boundary changes data in a way that inadvertently breaks logic in an unrelated deployment boundary.
Now, to be clear, I am not arguing against Cookies or localStorage - these are amazing technologies and they make life better. And, for the majority of us who build and deploy a front-end either as a single unit or as part of a monolithic application, this conversation is moot as there is only one large "ownership boundary". But, if you start breaking your front-end assets up into different build and deployment processes own by different teams, suddenly, client-side data storage becomes more interesting. Things like the localStorage API and the Cookies collection can start to look more and more like "integration databases." And, as such, I think these thought experiments are worth considering.