Overview

A content model consists of definitions which describe your project's content types, properties, graph associations, and the aspect-oriented features that Cloud CMS uses to ensure data consistency, integrity and validity when content is created, updated or deleted.

In Cloud CMS, all content modeling is done using JSON and more specifically, JSON Schema. JSON Schema provides an elegant and well-adopted model for describing the types for content objects, properties and other nested elements that make up your data.

Content models are powerful but you can use Cloud CMS without delving into them. Cloud CMS comes pre-loaded with a fully-built content model for most of the things you'd like to do. By default, Cloud CMS will allow a flexible schema so that you can create an JSON documents you like and things will simply work.

Dynamic Dictionary

For every branch you create within Cloud CMS, the product will maintain a compiled, runtime dynamic dictionary of all of your definitions. The compilation step occurs whenever you change one or more definitions. Compilation runs within the transaction and can fail if one or more of your definitions doesn't adhere to the rules. In this case, the dictionary safely falls back to the last known good state (in accordance with being transactional).

The Dictionary contains Definitions. Definitions come in several flavors, the main ones being:

  • Type Definitions
  • Association Definitions
  • Feature Definitions

A Definition is very similar to the definition of a word in the dictionary. The definition has a name (the word) and what it means (a description). In this case, the description is a JSON Schema-derived object which describes the valid properties, constraints and rules around your data. And the name is a QName.

A QName is a qualified-name. Simply put, it's a unique name that no other definitions in your branch are allowed to have. A QName consists of a namespace and a name and is expressed like this namespace:name.

You can use any namespace you like. And two different namespaces can share the same name. For example, you might define an article as a homepage:article. You could then define a second article type with a different namespace, such as landingpage:article.

The Content Graph

At the heart of Cloud CMS is a content graph consists of Nodes and Associations.

A Node is what you typically think of as a piece of content. A Node has JSON properties and zero or more binary attachments. Nodes can be anything you'd like. For example, they can be Articles that you display on your web site.

An Association is a relationship between two Nodes. An Association is a JSON document as well and you can use them to store any data you'd like, though it's typically data about the quality or characteristics of the relationship. Associations connect Nodes in the graph and maintain knowledge about the direction of the association (outgoing, incoming or mutual) and it's containment behavior (owned, linked, or child).

Whenever you put content into Cloud CMS, you're automatically populating the graph. For example, you might put a document into a folder within the user interface. Under the hood, this relationship looks like this:

- Folder (n:node) -> Has Child Association (a:child) -> Document (n:node)

The association maintains pointers back to the Folder (it's source) and the Document (it's target). The two nodes are both of type n:node and the association is of type a:child. These are the QNames for the Type Definition and Association Definition, respectively, that back each of these objects.

Type Definitions

A Type Definition describes the schema of a node in the content graph. A node is an entity. It's kind of like a noun in a sentence. It's a thing that is related to other things in the graph.

All content that you create in Cloud CMS has a Type Definition. The default Type Definition is n:node. This Type Definition is a bit special in that it doesn't enforce it's schema. You're allowed to create JSON documents of type n:node and they can have any arbitrary schema.

If you want to begin to enforce schema, you do so by creating your own Type Definition. A Type Definition, at it's core, is simply a JSON Schema document. Here is an example of a Type Definition for an article. It has a title, a body and a rating. It claims the QName my:article.

{
    "title": "Article",
    "type": "object",
    "properties": {
        "title": {
            "type": "string",
            "title": "Title"
        },
        "body": {
            "type": "string",
            "title": "Body"
        },
        "rating": {
            "type": "number",
            "minimum": 0,
            "maximum": 10
        }
    },
    "_qname": "my:article",
    "_type": "d:type",
    "_parent": "n:node"
}

The _type field is used throughout Cloud CMS to specify the Type Definition of a node. In this case, since our JSON above is for a definition node, the type is d:type which indicates that we are defining a brand new type.

All Type Definitions by default are assumed to extend the parent type definition n:node. For more information on setting up inherited schemas, see Inheritance below.

Association Definitions

An Association Definition describes the schema of a relationship between two nodes. Fundamentally, association instances are JSON objects that conform to the JSON schema of their association definitions. Association definitions allow you to specify the schema of your associations so as to store properties on them.

There are primarily two families of associations - a:linked and a:owned, describing linked and owned associations, respectively.

  • A linked association is one in which the relationship between the two nodes is optional. Deleting one of the nodes does not delete the other. The relationship between the nodes is descriptive but implies no ownership or containment. A linked relationship is such that deleting one of the nodes in the relationship has no impact on the other node.

  • An owned association is one in which a parent node "owns" a related node. Deleting the parent node implies required deletion of the owned node. And an attempt to delete the owned node should block since it is an owned or required element of the parent.

An example of an owned relationship might be a book with chapters. Deleting the book should delete all of the chapters. And an attempt to delete a chapter should throw an exception since the book requires the chapters for object model consistency.

Here is an example of an association that connects Pages to Articles. It stores order information on it as a number. This order information can then be used by the web site to order the display of articles on a page. Since the association derives from a:linked, the design is such that Articles can be reused across many pages:

{
    "title": "Page has Article",
    "type": "object",
    "properties": {
        "order": {
            "type": "number",
            "title": "Order"
        }
    },
    "_qname": "my:page-has-article",
    "_type": "d:association",
    "_parent": "a:linked"
}

Note that this association definition claims the QName my:page-has-article.

Feature Definitions

A Feature Definition is a cross-cutting concern or Aspect that you can apply arbitrarily to content instances or content types (to apply to all instances of a type). A Feature Definition is used to optionally describe additional schema that should be applied to a content or association type. Feature Definitions also endow content instances with special behaviors.

Feature Definitions are defined in much the same way as Types or Associations. Each Feature has a QName and the JSON Schema that should be adopted.

Here is a feature that defines a custom Authorable feature. We give it the QName my:authorable. The feature introduces an Author property to any content it is endowed upon.

{
    "title": "Authorable",
    "type": "object",
    "properties": {
        "author": {
            "type": "string",
            "title": "Author"
        }
    },
    "_type": "d:feature"
}

For a list of out-of-the-box features and more information about how they work, please see the Features section of the documentation.

Applying Features to Content Instances

To apply a Feature to a content instance (a Node), you simply add it to the _features block of your Node's JSON along with the enabled flag set to true.

Suppose we have an Article instance. The JSON for the content instance node might look like this:

{
    "title": "My Article",
    "body": "Here is the text for my article...",
    "rating": 3,
    "_type": "my:article"
}

The _type field tells Cloud CMS that this JSON conforms to the Type Definition for the QName my:article.

To add the my:authorable Feature to this content instance, we simply adjust the JSON so that it looks like this:

{
    "title": "My Article",
    "body": "Here is the text for my article...",
    "rating": 3,
    "_type": "my:article",
    "_features": {
        "my:authorable": {
            "enabled": true
        }
    }
}

When we save our changes, Cloud CMS will recompile the dictionary and this node will validate against a schema which consists of the Article Type Definition in conjunction with the Authorable Feature Definition. As a result, our node's JSON will support an additional property called author which is endowed from the Feature.

Cloud CMS will automatically make this property available in forms and any server and client-side validation logic will take this new property into account.

Apply Features to Content Types

In addition to assigning Features at an instance-by-instance level, you can also assign them to Type Definitions so that Features are mandated for any content instances. To do so, simply add your Feature's QName to the mandatoryFeatures block in your definition. Like this:

{
    "title": "Article",
    "type": "object",
    "properties": {
        "title": {
            "type": "string",
            "title": "Title"
        },
        "body": {
            "type": "string",
            "title": "Body"
        },
        "rating": {
            "type": "number",
            "minimum": 0,
            "maximum": 10
        }
    },
    "_qname": "my:article",
    "_type": "d:type",
    "_parent": "n:node",
    "mandatoryFeatures": {
        "my:authorable": {
            "enabled": true
        }
    }
}

By setting the Feature as mandatory on the Type Definition, it will automatically be applied to all content instances (nodes) that are of this type. This effectively accomplishes the same thing as the previous example where we applied the Feature at the instance-level, except that we've now done it for all instances of this type.

Note - be sure to use mandatoryFeatures when assigning type-scoped features. One might be tempted to use the _features block. However, the _features block is for instance-level assignment. If you did use _features, you would be assigning features to the Type Definition instance. Recall that in Cloud CMS, everything is content. Even content definitions! They're just content of types d:type, d:association or d:feature.

Inheritance

To take advantage of inheritance between definitions, simply set the _parent property to the QName of the parent definition that you wish to extend. Cloud CMS will utilize this at compile time to synthesize your definition as an aggregate of it's base types. Only one base type is permitted per definition.

If no _parent base type is specified, Cloud CMS will assume n:node for type definitions and a:linked for associations.

Properties

Cloud CMS supports the full range of JSON schema properties, constraints and validators. Properties can be nested as well and it is entirely encouraged that you use JSON to it's richest advantage. Cloud CMS will parse these properties at compile time and perform full validation across the entire JSON document.