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
The epi.shell.store.Registry object enables sharing object store data across components with minimal dependencies. 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 method for creating epi.shell.store.Patchable stores.
var patchable = registry.create("epi.cms.pageversion", url, options);An epi.shell.store.Patchable is an epi.shell.store.JsonRest store wrapped with cache and observable functionality, with additional methods to refresh and patch particular items in the store.
- patch – Updates an item in the object store cache and notes on the server. This minimizes requests to the server but still populates updates to the user interface through the store.
- refresh – Drops the item from the cache store if it exists, retrieves the latest version from the server, and puts it back into the cache.
Dependencies between object stores
Multiple object stores can depend on each other. An authoritative store with a subset of data stores as dependents lets you 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 large amount of data. Like the page tree, widgets need a subset of information placed in a separate store that depends on the content data store. 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 patch or refresh is called on the authoritative store, changes propagate to the dependent store.
Create a REST store
REST stores connect client-side Dojo stores to server-side controllers for CRUD operations over HTTP.
Server
On the server side, decorate the store controller with a RestStoreAttribute that has the store name as an argument. This name is used as part of the route to the store. The store should also extend the RestControllerBase class because this adds support for converting responses to JSON using the serializers configured in CMS. For example:
[RestStore("favorites")]
public class FavoritesStore : RestControllerBase {}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 Dojo with validation for cross-site request forgery.
When the REST store is created, 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 or module area to which it belongs. For example:
var route = routes.getRestPath({
moduleArea : 'Acme.Addon',
storeName : 'favorites'
});After calculating the route to the controller, create a REST store as follows:
var store = new JsonRest({ target: route });The store API provided by Dojo has methods that map to the standard CRUD operations that a REST store performs. These methods perform XHR requests against the server controller. The request and response bodies are expected 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 an item is added, updated, or removed from the store, the observer is notified if the modified item matches the query. Decorate a store as follows:
var observableStore = Observable(store);Use the stores
To create a store for use across multiple components, use epi.shell.store.Registry to create and register the store in an initialization module. Then retrieve it when needed and request the desired store.
With a store available, initialize the view for the component and look for other areas of the application that trigger an update in the view. For example, the following page version component requests versions for a particular page from the server and listens to changes to update the details in the list when an edit is made to 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, the onChange method is triggered.
Available object stores
The following object stores are available in CMS:
epi.shell.context– Used by the context manager to retrieve the current context. Caches current and previous contexts.epi.shell.profile– Stores information about the current user profile.epi.shell.metadata– Contains metadata for an object or type. Used to configure editing of content.epi.cms.contentdata– Contains content items such as pages and blocks with defined properties.epi.cms.content.light– Contains a light version of content, specifically properties needed for the page tree.epi.cms.category– Contains the CMS categories.epi.cms.contentversion– Contains information about versions of a content item.
Updated 17 days ago
