Motivation
At my current job, we strive to abstract away from frontend frameworks and libraries as much as possible. The application should be written in such a way that we could switch from Vue to React or Angular at any moment, even though that's unlikely to be needed. This approach can be debated, but I like it because it gives me more control over the logical part of my application, with the presentation layer being just a presentation layer.
We use Vue, along with Vuex ORM. This library provides an experience similar to interacting with an ORM (like TypeORM) but on the frontend, specifically when working with Vuex. It introduces concepts such as "model," "repository," "query," and so on. An important aspect is managing relationships, for example, when a user has posts and the posts have tags, with all these being separate entities. The code becomes organized and predictable, and with the use of TypeScript, relatively safe. Additionally, we get some bonuses like sorting and filtering "out of the box," without the need for extra coding.
After some time, I began working on a small pet project. The plan was to create a website using Vue and a mobile app using React Native. Since both are written in JavaScript, I thought it would be ideal to reuse as much code as possible. However, a problem emerged: Vuex ORM is, evidently, tightly coupled to Vue and Vuex. How could it be used with React?
I started looking for other libraries online and, unfortunately, found nothing with a similar DX. That's when I thought it would be great to have something like Vuex ORM, but not dependent on a specific store. Thus, the idea for Rattus ORM was born.
Getting Started
I based my work on the Vuex ORM Next code — the next iteration of Vuex ORM's lifecycle. Initially, I thought about writing everything from scratch, but decided that the experience gained and errors corrected by the Vuex ORM team were very valuable resources to utilize. I started by making the database work with the most obvious data source on the frontend: a plain JavaScript object. After detaching all connections with Vuex, I ran the tests and realized the idea was validated: practically, Vuex does not differ much from any object (apart from being made reactive by Vue).
Next, everything had to be prepared for different data sources. This led to the creation of the DataProvider concept — an abstraction meant to link the code written in Vuex ORM Next with any store. The main idea is that developers should implement the DataProvider interface, ensuring the store will work regardless of where the data is actually saved. This resulted in the @rattus-orm/pinia
package, echoing the idea of Vuex ORM and Pinia ORM.
What's Available Now
As of now, Rattus ORM can work with 5 frontend libraries and frameworks:
- Vue (Vuex, Pinia)
- React (MobX, Redux, Signals)
- Angular (RxJS)
- Svelte
- Solid There's also a DataProvider for Local storage, allowing data to be stored directly in the browser, preserved between sessions.
A plugin system has also been initiated. For example, the Plugin Zod Validate allows you to validate data in models using Zod.
Since the code is based on Vuex ORM Next, everything is fully compatible. In my pet project, I verified that simply replacing one library with another works as expected.
In short, adding each new integration involves two steps. First, decide whether a new DataProvider is needed or if reactivity works differently in a particular case (like with Svelte, Angular). If necessary, create a DataProvider. Secondly, write a middleware that allows using reactive data in the display, ideally working similarly everywhere.
What Does This Offer?
I often noticed that frontend applications are not very flexible. On one hand, this is normal, as each framework provides its standard for writing websites, optimizing, and improving our code. On the other hand, it seems incorrect to me that the final "display" process significantly affects the logical part. In my experience, I've already encountered situations where, in the middle of development, our team had to switch from one library to another. If there were ways to reduce the time spent on migration, I would use them.
However, it's not just about universality and abstraction. An application that's ready for anything hides details behind abstractions, and at the higher levels, it shouldn't need to know where the data came from. Vuex ORM always knew it was an HTTP request, the result of which was saved in Vuex. Rattus, however, only provides access to them, not participating in the "creation" of data. This is why you can easily transfer the entire logical part to other tracks without fearing an error on something specific to a particular library.
Currently, the core of Rattus is at version 0.1. I can say that it's stable enough to try out, but still, the project is quite young. If this story has piqued anyone's interest, I would be very happy to collaborate on its improvement.
Top comments (0)