Distributed Store

The distributed store is a key factor underlying Finsemble’s inherent scalability. Finsemble applications are assembled from multiple components, each of which lives in an OpenFin window. The naturally-occurring process boundaries in a browser window are what allow unrelated components to co-exist in a loosely coupled manner. The distributed store then provides the perfect mechanism for components to share data dependencies without compromising on their loose coupling.

The store-based approach is elegant in that no component ends up with an explicit dependency on any other component. Components in this architecture can be designed so that they depend only on a shared data model. Operating in this environment, components can come and go throughout the lifetime of a user’s session. When a component is instantiated, it immediately gains state via the store. Values can accurately be displayed to the end user and the component can begin participating with other like-minded components in modifying that state.

Creating a distributed store

Stores are used to save and retrieve data either locally or globally. This data is not persistent. You can add listeners at multiple levels (store level (mystore) or at an individual attribute level within the stores values value1.value2) and get the updated data as it's updated in the store. Fields are stored within the store as key/value pair.

To create a store do the following:

var values = {
    item1:"value1",
    item2:{
        item3:"value2"
    }
}

FSBL.Clients.DistributedStoreClient.createStore({store:"store1",global:false,values:values},function(err,storeObject)
{

});

This returns an instance of the store that you can now interact with. If a store already exists by the provided name, a store is not created and the previously created store is returned.

createStore has the following options:

  • Store - The name of the store that you would like to create.
  • Global - If true, the store can be accessed by any component/service within Finsemble. If false, the store can only be accessed by the current component/service.
  • Values - The default values you'd like to have in the store.

If you know a store already exists, you can do the following:

FSBL.Clients.DistributedStoreClient.getStore({store:'storenamehere'},function(err,storeObject){

});

Using the distributed store

Getting values

getValue allows you to get a value from the store. You must provide the value that you'd like to retrieve and a callback. If the value is a top level attribute then you can just pass in a string like so:

store.getValue('item1',function(err,value){});;
// value1

Here, we get the value of item1 (value1) set in Creating A Distributed Store. You can also get nested values from your store:

store.getValue('item2.item3',function(err,value){})s;
// value2

getValues is similar to getValue, however, it returns an array of values.

store.getValues(['item1','item2.item3'],function(err,value){});
// [value1,value2]

Setting values

Just like getting a value, you can set a value in a store in two ways:setValue and setValues. You can set a value at any level within your store. Example 1 shows how to set a value at the top level of your store. Example 2 shows how to set a nested value. When a value is set, listeners are fired at every level that is changed. Listeners are covered further down the page. You can set a nested value that does not exist and it will create the value tree to that point.

//Example 1
store.setValue({field:'field1',value:"new value"},function(err{

}));
//Example 2
store.setValue({field:'field1.field2',value:"new value2"},function(err){

});
store.setValues([{field:'field1',value:"new value"},{field:'field1.field2',value:"new value2"}],function(err){

});

Removing values

Removing a value is very similar to setting a value; however, you only need to specify the field you want to remove. Note: Right now, this just sets the value of the removed value to null.

store.removeValue('field1',function(err){

});
store.setValues([field:'field1','field1.field2'],function(err){

});

Listeners

Listeners are where the power of the store comes into play. Listeners allow you to receive notifications when a specific field has changed. Just like setting/getting a value, you can listen at any level you want to. addListerner takes the following arguments:

  • Field - This is the attribute within your store to which you're listening for changes. Listeners will get changes to anything to the current level and below. For example, if I have the following object :
{
    field1:{
        field2:{
            item1:"value1",
            item2:"value2"
        }
    }
}

and I have a listener on field1, my listener will fire for any changes to item1,item2, field2, or field1. This is an optional parameter. If a field is not provided then a listener is placed at the store level.

  • Function - This is the function you want triggered on a change to the store. You should not use an anonymous function here. Since anonymous functions can't be references, you won't be able to remove any listener that is created with one.

  • Callback - A callback will get called after the listener is set. It's optional.

Here are some examples of how to useaddListener:

function myFunction(err,updatedData){
//handle your changes here
}

//Store level listener:
 store.addListener(myFunction,function(err){});

//Top Level Listener:
 store.addListener("someField",myFunction,function(err,storeValues){
 });

//Nested Listener:
 store.addListener("someField.someField2",myFunction,function(err,storeValues){
 });

We also have an addListeners function that takes array of values for the field. The field parameter can take data in the following formats:

var myFunction = function(err,data){

}
var commonListener = function(err,data){

}

//With this method you can specify a different listener for each field provided.
store.addListeners([{field:'field1',listener:myFunction},{field:'field2',listener:myFunction}],null,cb);

//If you only want to specify specific listener functions for some of the fields and use a common listener for others then you would want to use this method.
store.addListeners([{field:'field1'},{field:'field2',listener:myFunction}],commonListener,cb);

// If you just want to use a common listener function for all fields, this is the method to use.
store.addListeners(['field1','field2'],commonListener,cb);

Removing listeners

To remove a listener, you just need to provide the field and the function used for that listener. The callback returns a boolean which states if the listener was removed.

var myFunction = function(err,data){
}
//Store level remove
store.removeListener(myFunction,function(err,bool){});
//field level remove
store.removeListener({field:'field1'},myFunction,function(err,bool){});

Removing a store

Removing a store is as easy as stating it's name and it's scope. If you don't specify scope, Finsemble will try to remove it locally first and then look globally. If the the removal is successful, the callback will return true for the bool property.

FSBL.Clients.DistributedStoreClient.removeStore({store:"store1",global:true},function(err,bool){});

Further reading

Advanced information about the Distributed Store can be found in the Distributed Store Client documentation.

Read the Router tutorial to better understand how Finsemble sends and receives messages.