KissJS data layer provides a way to manage data locally and to proxy data from a KissJS server (or a REST server). It consists of:
- kiss.data.Model: to define your models
- kiss.data.Collection: to store data according to your models
- kiss.data.Record: to manipulate instances
- kiss.data.Transaction: to perform batch updates over multiple collections and multiple records
The data layer is a work in progress and might change rapidly according to new project requirements.
Classes
Namespaces
Members
# static relations
kiss.relations
This module handles the relationships between models inside a NoSQL environment.
Context:
In modern applications, we are used to update the database as soon as a single field is modified. It brings a better user experience than filling a bunch of fields then click on a "Save" button. It also prevents too much data from being lost in case of a crash when a user is filling tons of data.
Problem:
In NoSQL, databases are denormalized: data is redundant accross records. When the field's value of a record depends on foreign records, we need some logic to keep data in sync between records.
To keep things simple, we will say that some fields can be a "source" for other fields. We simply call them: "source fields". When a source field is updated, we need to update all the fields that depends on this source.
In business applications, we have identified 2 common scenarios:
-
lookup fields: A lookup field takes its value inside another field of a foreign record. When the value of the foreign source field is updated, the lookup field must be updated too.
-
summary fields: A summary field summarizes (aggregates) data of multiple foreign records. When the value of a single foreign source field is updated, the summary field must re-compute the whole aggregation
Solution:
When a field is updated, we track all the mutations that are triggered:
- inside the same record (because of computed fields)
- inside foreign records (because of the relationships between records, defined by special "link" records)
To fit all possible scenarios (1-1, 1-N, N-N), KissJS manage all relations as N-N. The links are maintained in a single external table: the "link" table.
When a lookup field or a summary field needs to be computed, the process is:
- search for the foreign records (thanks to the link fields of the record)
- get data from foreign records
- compute the new field value, but do not update the field immediately
- instead, add the upcoming change to a transaction
Because a field change can trigger a chain reaction over other computed fields, the process is called recursively until there is no more field to update.
At each cycle, the change is added to the transaction. At the end, the transaction is processed, performing all required database mutations at once.
To boost performances, an architectural choice was to load every links into cache, amd maintain this cache each time a link is added or removed. For bigger applications with tens of thousands of records and links, this choice might need some optimization process.
Methods
# static addMethodToAllModels(methodName, method)
Add a method to all models
This method is useful to plug a method to all records after they are already instanciated.
A typical use case is when a plugin needs to add a global feature to all records. We can call this method in the plugin initialization.
Parameters:
Name | Type | Description |
---|---|---|
methodName |
string
|
|
method |
function
|
Example
kiss.data.addMethodToAllModels("getFields", function() {
return Object.keys(this).join(", ")
})
console.log(myRecord.getFields()) // "firstName, lastName, birthDate"
# static addPropertyToAllModels(propertyName, getter)
Add a property to all models
This method is useful to plug a property to all records after they are already instanciated.
A typical use case is when a plugin needs to add a global feature to all records. We can call this method in the plugin initialization.
Parameters:
Name | Type | Description |
---|---|---|
propertyName |
string
|
|
getter |
function
|
The getter function, which receives the instanciated record as input parameter |
Example
// To add a property
kiss.data.addPropertyToAllModels("WORKFLOW_STEP", function(record) {
return kiss.global.workflowSteps[record["workflow-stepId"]]
})
console.log(myRecord.WORKFLOW_STEP) // "Analysis"
// To add a property which has methods
kiss.data.addPropertyToAllModels("workflow", function(record) {
return {
start: function() {
console.log("STARTING WORKFLOW...")
},
vote: function(recordId, stepId, decisionId, comment, actors) {
console.log("VOTE FOR..." + recordId)
}
}
})
// Usage
myRecord.workflow.start()