⚠️ This is at a very early stage and more a prove of concept. I am looking forward to any suggestions and help.
Pairis is a simple data storage alternative to traditional databases for smaller projects. Data structures can be modeled like ordinary TypeScript classes with properties, methods, getters and setters, with the difference that all properties are automatically saved. When changes are made, events are automatically triggered, which in turn can be subscribed to. The fact that all data is stored in key/value pairs makes Pairis perfect for use with localStorage, for example. However, due to its modular design, storage types can be easily changed at any stage of development.
//define a model with properties as you would normally do with typescript
class Animal extends Model {
static list = 'animals'
species!: string
legs = 4 //default value
}
//using it like so
const tallAnimal = Animal.use()
tallAnimal.species = 'Giraffe'
//or using set()
tallAnimal.set({
species = 'Giraffe'
})
//retrieving
tallAnimal.species //'Giraffe'
tallAnimal.legs //4
Pairis stores all the data into key/value pairs. It uses 'lists' to combine all datasets of a model. Each dataset is stored under a unique identifier which is generated automatically. The keys are in turn stored in a list, which is basically an array. Here is an example of what this could look like (where uidXY is a valid UUID v4 key):
{
"homes":["uid4","uid5"],
"uid4":{"name":"CoolWG","city":"Berlin"},
"uid5":{"name":"NiceHouse","city":"London"},
"flatmates":["uid1","uid2","uid3"],
"uid1":{"name":"Robin", "home":"uid4"},
"uid2":{"name":"Luca", "home":"uid4"},
"uid3":{"name":"Aron","home":"uid5"},
}
Pairis uses 3 basic types of relationships, which can be combined in a logical manner:
- One-To: points to one foreign object. The key is stored locally.
- To-One: again points to one foreign object but the key is stored into the foreign object.
- To-Many: points to more than one foreign object. keys are stored in foreign objects.
In order to make relationships work, we need to 'introduce' our models to each other with myModel.introduce()
.
//basic relationship example
class Place extends Model {
static list = 'places'
description!: string
//to-Many:
animals: Animal[]
}
Place.introduce()
class Animal extends Model {
static list = 'animals'
//one-To:
place:Place
}
Animal.introduce()
Relationships are defined and guessed by property names. Therefore it is important to retain some naming convetions.
- Propertys that are pointing to a single dataset (One-To) always have the same name as the class they are pointing to, in lowercase.
- Propertys that are pointing to more than one datasets (To-Many) have the same name as the list name of the model they are pointing to.
- To-One relations are recognized by propertys starting with '$', followd by the class name in lowercase.
{
"animals":["uid1"],
"places":["uid2"]
"uid1":["species":"giraffe"]
}
Every dataset is fitted with a subscribe function, that triggers on any value change.
tallAnimal.subscribe(()=>{
//...Tall Animal has changed. Now do something with it...
})
It is of course easily possible to use custom methods on datasets.
class Window extends Model {
opened = false
open() {
this.opened = true
}
close() {
this.opened = false
}
}
const window = Window.use() //window.opened => false
window.open() //window.opened => true
window.close() //window.opened => false
const store = new PairisStore(localStorage)
@persist(store)
class Car extends Model {
//...
}