HomeDev GuideRecipesAPI Reference
Dev GuideAPI ReferenceUser GuideLegal TermsGitHubNuGetDev CommunityOptimizely AcademySubmit a ticketLog In
Dev Guide

Store architecture

Describes the implementation of object stores in Optimizely Content Management System (CMS) and their recommended pattern for use.

The Optimizely Content Management System (CMS) user interface extensively uses the object store API available in the Dojo Toolkit.

Implementation in the CMS user interface

To create an environment where you can share object store data across components with minimal dependencies, use the epi.shell.store.Registry object. This object maintains a dictionary of key and object store pairs.

For example:

var registry = dependency.resolve("epi.shell.store.Registry"),
  store = registry.add("epi.cms.pageversion", new Memory());

It also has a convenient method for creating epi.shell.store.Patchable stores.

var patchable = registry.create("epi.cms.pageversion", url, options);

An epi.shell.store.Patchable is essentially an epi.shell.store.JsonRest store, which is wrapped with cache and observable functionality and has additional functionality to refresh and patch particular items in the store.

  • patch – Updates an item in the object store cache and notes on the server. The intention is to minimize requests to the server but still populate updates to the user interface via the store.
  • refresh – Will cause the item to be dropped from the cache store, if it exists, and the latest version retrieved from the server and put back into the cache.

Dependencies between object stores

You can have multiple object stores that depend on each other. If you have an authoritative store with a subset of data stores as dependents, you can update the authoritative store on the client and push those changes to the dependent stores with the patch method so they do not query the server again.

For example, the content data store contains the content (and associated property values) that was viewed or edited, which can be a lot of information. Like the page tree, widgets need a subset of information placed in a separate store that depends on the content data store. You create this dependency using the addDependentStore method as shown in the following example:

var registry = dependency.resolve("epi.shell.store.Registry"),
  authoritative = registry.create("epi.cms.contentdata", url, options),
  dependent = registry.create("epi.cms.content.light", url, options);
    
  authoritative.addDependentStore(dependent);

When you call patch or refresh on the authoritative store, changes are populated through to the dependent store.

Create a REST store

Server

On the server side, decorate the store controller with a RestStoreAttribute that has the store's name as an argument. It uses this name as part of the route to the store. The store should also extend the RestControllerBase class because this supports converting responses to JSON using the serializers configured in CMS. For example:

[RestStore("favorites")]
public class FavoritesStore : RestControllerBase {}

You should name methods on the store after the HTTP method for which they are created. For example:

[HttpGet]
public ActionResult Get(int id) {}

Client

On the client side, create store instances from the epi/shell/store/JsonRest class, as this extends the standard REST store included in the Dojo with validation for cross-site request forgery.

When you create the REST store, provide a route to the server-side controller. The epi/routes module provides helper methods for calculating the route based on the store name and package/module area it belongs to. For example:

var route = routes.getRestPath({
  moduleArea: 'Acme.Addon',
  storeName: 'favorites'
});

You can create the REST store after calculating the route to the controller as follows:

var store = new JsonRest({ target: route });

The store API provided by Dojo has methods that map easily to the standard CRUD operations that a REST store may perform. In the case of the REST store, these methods will perform XHR requests against the server controller. The request and response bodies are to be JSON-formatted.

See the dojo/store/JsonRest implementation for more detailed information.

Observable

The dojo/store/Observable decorates a store by adding an observe method on the results object returned by the query method. Use this to monitor the store for changes that may affect the results returned by the query. When you add, update, or remove an item from the store, then the observer is notified if the modified item matches the observer's query. Decorate a store as follows:

var observableStore = Observable(store);

Use the stores

If you are creating a store to use across multiple components, use epi.shell.store.Registry to create and register your store in an initialization module. Then, you can retrieve it when needed and request the wanted store.

When you have a store, you can initialize the view for your component and look for other areas of the application that trigger an update in the component's view. For example, the following page version component requests versions for a particular page from the server and listens to any changes to update the details in the list when you edit the version.

var registry = dependency.resolve("epi.shell.store.Registry"),
        store = registry.get("epi.cms.contentversion"),
        query = store.query(
          {
            id       : 3,
            language : "en"
          });
    
          query.observe(this.onChange.bind(this));
          query.then(function(results) 
            {
              this.view.data = results;
            }
        );

If another part of the application calls add, put, delete, refresh, or patch, and the modified data matches the query, then the onChange method is triggered.

Available object stores

Store KeyDescription
epi.shell.contextUsed by the context manager to retrieve the current context. Is a cache of current and previous contexts.
epi.shell.profileStores information about the current user profile.
epi.shell.metadataContains metadata for an object or type. This is used to set up editing of content.
epi.cms.contentdataContains content items such as pages and blocks with defined properties.
epi.cms.content.lightContains a light version of content, specifically containing properties needed for the page tree.
epi.cms.categoryContains the CMS categories.
epi.cms.contentversionContains information about versions of a content item.