Concepts

This chapter explains the core concepts behind ACE.

Datamodel

The core object managed by ACE is the Content.

Content and Aspects

The purpose of the typing is primarily so that systems that consume the content can assign semantics to the objects, but types are also used for some features in ACE itself. Aside from assigning semantics, a type may also define the types of the fields in an object. The type system allows for essentially arbitrary JSON as well,by supporting a special type for untyped JSON.

Data in Content is organized into named Aspects. An aspect with the same name is intended to have the same meaning in every Content it is used, so for instance the aceCategorization aspect is used to represent user-supplied categorization for all types of content. Most aspects will represent some kind of cross-cutting concern like that.

Each content also has a Content Type, which is a type with some special properties. A content's type can't be changed after creation. A Content Type can mark some aspects as mandatory, so that content can't be created without including those aspects but it cannot restrict which aspects can be added to the content.

Aliases

To identify content, ACE uses aliases. An alias consist of two parts: a namespace and a name. The namespace is used to separate aliases used for different purposes, while the meaning of the name depends on the specific namespace used.

In Content, aliases are represented as a string with a type named "alias".

Main alias

A content may have any number of aliases, but always has at least one alias, namely its main alias. The main alias always belongs to the contentid namespace.

Aliases are typically user defined, but if no main alias is supplied upon content creation, a universally unique main alias is automatically generated and assigned to the content.

Versions

Content in ACE is versioned. This means that when content is created or updated, a new version is created. Once created, a version can never change aside from being deleted. Every version of a content has a unique ID that can be used to reference it. Just like content, aspects are also versioned (except "Content Metadata").

In Content, versions are represented as a string with a type named "contentVersionId".

Revisions

Every content has a revision, in order to keep track of content changes. Whenever anything in the content is changed, a new revision is created. However, unlike versions, the revisions are not persistent, meaning that only the latest revision exists at any given time.

Unversioned Aspects aka Content Metadata

There are "unversioned Aspects" that belong to the content itself rather than a specific version of a content. These Aspects are called "Content Metadata Aspects".

Content Operations

The core content operations supported by ACE are Create, Read, Update, and Delete. See also workspaces, described later, for a variation on creating and updating.

Creating

Creating simply creates a content with one version and an initial revision. It involves permissions and the write pipeline, described later.

Read

Reading is immediately more involved: since a content is versioned, you have to decide what version you want. You can fetch a version if you know the version ID, or you can resolve an alias to a version.

Reading also involves permissions, views and variants, described later.

Update

Updating a content usually involves creating a new version, but sometimes only certain metadata is updated. Whether a new version is created or not, the revision of the content changes.

When updating a content, it is possible that someone else has also updated the same content, which introduces a potential for conflicts. This is handled with optimistic locking: when you update a content, you include an ETag which is used to determine if you had the latest changes already. If you didn't, you get a conflict response and have to resolve the conflict before resubmitting with a new ETag. Internally this is based on the revision, but as a user you should only worry about the ETag.

Like creating, updating involves permissions and the write pipeline. Revisions are used to deal with eventual consistency.

Delete

This is essentially just an update, only instead of creating a new version it removes all versions if successful.

Eventual Consistency

ACE is an eventually consistent system: different parts of the system may see different versions of content at any given point in time, but will converge to the same version over time. This mostly shows up in two ways: searching and caching.

Since the index that is searched is updated asynchronously, the content in the index may be out of date. And since the Content API has a cache, the content you get from it may be out of date.

To get around some of the issues this can lead to, it is possible to ask the Content API to make sure the content you get is at least as up to date as you are. This is done by telling the system to use a revision of the content at least as recent as yours. There is no similar system for the search service.

Variants and the Read Pipeline

To make it easy to reuse content in different contexts, ACE uses Variants. A variant is a way to request content in a form suited for a specific consumer of content, rather than forcing the consumer to understand all kinds of content.

In essence a variant is a set of rules for mapping different types of content to the variant's representation of content.

Any Content read with the Content API is pushed through a data processing pipeline. The pipeline consists of a series of stages in which the stored Content is gradually transformed into the Variant requested by the client.

Mapping Aspects

The first stage is the aspect mapping stage, where each aspect that has a mapper defined in the variant gets processed by that mapper. Aspects that were not discarded by their mapper are then reassembled into a new data shuttle that is sent to the composer stage.

An aspect mapper has access only to the aspect it is mapping, and nothing else. This is to ensure the output can be cached.

Composing content

The second and final stage is the content composer stage. A content composer gets a data shuttle as its input, and can remove, modify or add any aspects it likes. It also has access to a content service, so it can even read other content and include that in the response.

Since a composer can access things other than the content, ACE doesn't try to predict how cachable its output is. Instead the default is to never cache, but there is a mechanism for the composer to set custom cache headers.

Write Pipeline and Lifecycle Hooks

The purpose of the write pipeline is to allow content to be transformed and validated before being updating the database. This can be used for things like adding custom aspects, implementing a custom permission system, or setting default values that the client didn't set.

The write pipeline has two stages: pre-store callbacks can modify the data to be stored, and if there is a conflict additional on-conflict callbacks can attempt to resolve the conflict.

In the special write case when a content is deleted, pre-delete callbacks may prevent the delete. The other kinds of callbacks are not involved in deletes.

All these hooks can abort the operation by throwing an exception, which may optionally set the status that should be reported to the client.

Modules

ACE has a module system that can be used to organize read and write pipelines. The content service can be configured with an ordered list of modules that add functionality.

There are two ways to map callbacks (mappers, composers and lifecycle hooks): globally, or in a module. When the system looks for a callback, it first looks in the global configuration. If a matching variant or lifecycle hook is found, that configuration is used and modules are ignored. If the global configuration doesn't have the variant or a lifecycle hook, we scan the modules in order until we find a callback to apply.

To support searching among content, content can be indexed and then searched using Solr.

Indexing

Indexing is done by the Solr Indexer component, which listens for changes to content and indexes anything that is changed.

Each instance of the Solr Indexer is responsible for one Solr collection (index). Each collection has a corresponding variant (which may be shared by multiple collections), and may also be restricted to only include content that is published on certain views. By default, all versions of a content are indexed in Solr regardless of view.

The variant for a collection maps the content to be indexed to a Solr JSON document, and that is then supplied to Solr for indexing. If content is not available in the collection's variant, that content is not indexed.

Searching

Searching for content can be done either directly in Solr, or using the ACE Search Service. The Search Service is a wrapper around Solr that uses Solr query syntax, but adds a few features:

  • Automatic filtering by permissions
  • Filtering by view (optional)
  • Filter on availability in variant
  • Inlining content in results

Filtering by permissions can be disabled for specific views, so that the search service can be used without authentication for content meant to be public.

Views

Views are a way of naming versions of content. A version of a content can be assigned to, or removed from, a view, so that when the content is requested on that view it resolves to that version (or no version, if no version assigned).

Views are used to make different versions of content available for different users. The simplest example is when you want one version to be live on a website, while a new version is undergoing editorial review. The live version would be on the view used by the website, while the version undergoing editorial review is on another view only visible to editorial staff.

The system keeps track of a history for each view and each content, so you can see which version was on the view at a given point.

Symbolic views

A symbolic view associates a view A with another view B, rather than a specific version, so that the version on A resolves to whatever the version on view B currently is.

Time state

The presence of a content on a view can also be controlled by time state, i.e. an on time (before which the content is not available on the view) and/or an off time (after which the content is not available on the view).

Workspaces

Normally when you save content in the Content API, a new version is created that is added to the version history, indexed and so on. Sometimes you just need to make content available on the server but not store it permanently, for example to autosave work in progress, or preview it in a different application. The way to do this is to save it to a workspace, which is an ephemeral content store with no persistence guarantees and no history.

Workspaces are automatically created when content is saved to them. When you read content from a workspace, it transparently supplies either the version stored in the workspace, or the latest version of the content if it is not stored in the workspace.

In addition to storing content in a workspace, you can also assign an alias to a content in a workspace. Such an alias can only be resolved on a workspace. However the alias is reserved so it can't be assigned to any other content until the content is removed from the workspace or the alias is released.

Files

The system includes a file service, for dealing with large binary data. The file service comprises two sub-services: the file service, and the file delivery service. The file service simply stores files, and is completely independent of content. The file delivery service lets you access files that are associated with content.

File Service

The file service lets you upload files, and returns an identifier that lets you retrieve or delete the file. Files can never be modified, instead you upload a new file. The file service is typically only used by other ACE services to manage files.

Files on content

Files can be associated with content using the aceFiles aspect, which associates content-specific paths with file identifiers.

File Delivery Service

The file delivery service serves files referenced from an aceFiles aspect. As a special case, files that are also registered as images are not accessible to avoid the original of an edited image being available.

Images

The system includes an image service that supports image operations like cropping, scaling, rotation and pixellation.

A content can represented an image by including an aceImage aspect that selects a file in the aceFiles aspect as being the image file. Edits to the image are held in the aceImageEdits aspect.

The image service looks at these aspects, selects an appropriate crop, and delivers an image scaled to the size requested by the user.

Atex Metadata

The Atex Metadata system is a way to categorize content in such a way that it can be efficiently searched and cross-referenced. The system allows content to be categorized along many different axes of interest.

Taxonomies

The Atex Metadata system centers on entities, which are what content is categorized with. Entities may have other entities as children (e.g. "sport" may have "bowling" as a child), and belong to dimensions which group related entities (e.g. IPTC subject, person and author could all be different dimensions). Dimensions are grouped into taxonomies, which exist so that e.g. two different publications may use different subsets of the available dimensions.

Taxonomy Service

The Taxonomy Service allows access to the taxonomies, dimensions and entities, by either listing them, or using autocompletion in a specific dimension. The service does not support creating new entities, that must be done using content.

Categorization

Content is categorized by using an aspect called aceCategorization. It contains a list of the taxonomies the content is categorized in, and the entities the content is categorized with.

Permissions

In ACE, permissions are granted to users based on the type of the content, the security context(s) the content belongs to, and the roles the user has in those contexts. Types have already been covered earlier, but security context and role are specific to the permission system.

Permissions

Permissions are simply things you're allowed to do to a content, like create, update, delete, assign to view, etc.

Security Context

Content belongs to one or more security contexts. Security contexts are assigned when content is created and can be changed later. They are essentially just names that are used to determine which roles a user has in relation to content belonging to them. Security contexts have no hierarchy or other structure to them.

Roles

A role grants permissions on types. It basically lists for each type what permissions it grants on those types. They are represented as content of type aceRole.

User roles

For each user there is a content with a aceUserRoles aspect, that maps security contexts to the roles that user has in those contexts.

Naming conventions

All names used by the system, or provided by the system for your use, are prefixed with 'ace'. Names starting with 'ace' are reserved in most contexts except where explicitly stated not to be.

We recommend that you adopt a similar naming convention, but with a different prefix, for your project, to reduce problems when integrating with third party projects.

Aliases

Alias namespaces starting with an underscore ("_") - such as _config, _defaults, _role and _type - are used for ACE core functionality, and should only be used for content meant for these namespaces (such as a configuration content in the _config alias namespace).

Configuration content provided by ACE will use 'ace' as prefix in the alias part. Example: _config/aceCallbacksConfig.

Types

Types included in the system are prefixed with 'ace', followed by a name in Pascal case , e.g. 'aceImageFormats'. Because types are often used as field names (when used as aspects), type names are valid Javascript identifiers.

Variants

System variants are prefixed with 'ace', e.g. the name of the variant used for indexing is 'aceIndexing'. Because variants are sometimes used as field names, they are valid Javascript identifiers.

Configuration content

Configuration content names are not usually used as field names, so in the interest of readability we use '.' for extensions.

Configuration for variants is stored in content named for the variant with an extension '.variant', e.g. the indexing variant is configured by 'aceIndexing.variant'.

Global configuration where there is usually only one instance is stored in a content with the same name as the primary aspect in the config, e.g. 'aceCallbacksConfig' for the content the system reads the 'aceCallbacksConfig' content from.

Callbacks intended for a specific variant is named for that variant, followed by a '.' (period), followed by a descriptive name starting with lowercase. E.g. the mapper for the aceCategorization aspect in the aceIndexing variant is called 'aceIndexing.categorization'. This does not mean such callbacks can't be used for a custom variant, it's just a hint that they are specialized for a certain kind of variant.

Generic callbacks are prefixed with 'ace.' followed by a descriptive name in lowercase.

Views

System views are prefixed with 'ace'. Because views are sometimes used as field names (e.g. in time state), they are valid Javascript identifiers.

Permissions

System permissions always have names starting with capital letters.