The Reference Data Service
Sitecore 9.0 introduces the evolution of xDB, xConnect. xConnect is a complete (almost) rebuild of the customer behavior collection and reporting capabilities of Sitecore (plus more). These capabilities are now delivered through a new set of separate applications. In this post, I’ll be introducing you to the Reference Data Service, which is a completely new capability that was introduced with xConnect.
The Reference Data Service is a repository of definition data. Definitions can be anything that is required to be referenced within the xConnect environment. To properly understand what the Reference Data service is, we need to understand the problem it solves.
In previous versions of Sitecore, all the xDB capabilities had access to the content tree where all the marketing definitions resided, as these capabilities were developed inside a Sitecore CMS instance. The marketing definitions (such as goal definitions, page event definitions and outcome definitions as well as others) are required so the processing role can lookup the definitions by ID. When we developed xConnect, we wanted to create completely new applications that weren’t based on the standard Sitecore CMS, as it contains much functionality that isn’t required for these new applications. For example, we don’t need the rendering pipelines, or all the normal Sitecore UI.
Because we wouldn’t have the Sitecore kernel available, we wouldn’t have a content tree where all the marketing definitions are stored. Instead, we needed a way to store those definitions within the xConnect environment. And that is the problem which the Reference Data Service solves. It allows us to store the marketing definitions and access them within xConnect.
You can also store your own definitions inside the Reference Data Service, so you can reference your own domain objects in the same way Sitecore does the xConnect collection model. To illustrate how to do this, let’s define a scenario.
Let’s say we’re working for a company that sells guided tours. When we want to trigger an xConnect event, such as a goal or an outcome, we want to be able to reference the tour which the event is relevant for, without having to replicate all the tour data into the event object. Instead, we’d like to reference the tour by some ID instead, then when we need additional tour data during processing, we can lookup the tour using that ID and one of the Reference Data clients.
To store our own definitions, we need to create the classes which will hold the definition data. Definitions are localizable, so in additional to defining the common data which is shared across all cultures, we also need to define the culture specific data. Both of these classes are just POCOs (Plain Old C# Objects). So let’s start with defining the common and cultural classes for the tour definition.
public class TourCommon
You might notice I’ve not included an ID in the
TourCommon class. This is because we’ll use the ID as part of the
DefinitionKey, which is used to identify a single definition.
As with the rest of xConnect, the Reference Data client can be used from any .net application, not just from inside of a Sitecore CMS instance. There are actually 2 Reference Data clients, a read-only client and a read-write client. You can use whichever is appropriate for your context.
Both clients are available in the Sitecore IoC container, so it’s available through dependency injection (depending on the class in question), or by accessing the IoC container directly:
The clients are also available inside many of the xConnect roles. They’re present in the IoC container and various services also expose them in other places.
For example, if you are creating a marketing automation activity the read-write reference data client is available from the
ActivityServices property under a property named
Now we have access to the client, we can identify a definition we want to store.
DefinitionKey class is used to identify a single definition. It consists of 3 parts:
- A definition type
- A definition moniker
- a version number
The definition type is a name used to categorise definitions. This is done to allow reuse of a moniker across different types, kind of like a namespace. It’s also used to retrieve all the definitions with the same type. Before we can create a definition key, we must get a definition type key. Both the definition type key and the definition moniker can be considered text values, which are case-sensitive.
There are 2 different ways to get a definition type key, depending on whether you’re saving definitions or retrieving definitions.
If saving definitions, you’ll be using the read-write client, and you’ll want to use the
EnsureDefinitionKey method. This method will retrieve the key if it exists, or create it and return it if it doesn’t.
If you’re only retrieving definitions, and don’t want to accidentally create a definition type if it doesn’t already exist, then use the
GetDefinitionKey method which is available on both clients.
var definitionTypeKey = await client.EnsureDefinitionTypeAsync("Tour");
Keep in mind, the definition type key is case-sensitive.
Once we have the
DefinitionTypeKey, we can instantiate the
DefinitionKey used to identify the definition.
var definitionKey = new DefinitionKey("the-tour", definitionTypeKey, 4);
The last parameter is the version number. That’s right, definitions are versionable, so to identify a single definition, the version number must also be provided.
Saving a definition is pretty straight forward. Simply instantiate the definition using the definition key, then call the
var definition = new Definition<TourCommon, TourCulture>(definitionKey)
When saving the definition, if the version (given by the version number) already exists, the version will be overwritten. If the version does not exist, it will be added.
Note how the culture data was defined above. The
CultureData property of the
Definition<TCommon, TCulture> class is a dictionary which uses a
CultureInfo class as the key and an instance of the
TourCulture class as the value.
To retrieve a definition we need to identify it just like when saving the definition, however in this case, the version is optional. Often you will want to retrieve the latest active version of the definition, rather than specify the exact version. And you may not know the current latest version ahead of time.
To specify the definition you want to retrieve, use an instance of
DefinitionCriteria. It still uses the definition type key like above, and the moniker, but the version is optional.
var criteria = new DefinitionCriteria("the-tour", definitionTypeKey);
Keep in mind the definition moniker is case-sensitive.
The cultural data returned is determined by the
Culture property of the definition criteria. If this is set to
null, then all cultural data available for that version will be returned. If you only want the cultural data for a single culture, provide that culture in the
var criteria = new DefinitionCriteria("the-tour", definitionTypeKey);
Now we can retrieve the definition.
var definition = await client.GetDefinitionAsync<TourCommon, TourCulture>(criteria, true);
The second boolean parameter determines whether to get the latest active version of the definition.
With the definition in hand, we can now access the POCOs we defined above through the
CommonData property for the class holding the data common for all cultures, and the
CultureData dictionary for culture specific data.
var tourCommonData = definition.CommonData;
The new Reference Data Service is not particularly complex, but this post is only scratching the surface. Nonetheless, with this information, you can now make use of the Reference Data Service to store your own domain objects to be used as lookups inside of xConnect. For more details information, check out the official documentation at https://doc.sitecore.net/developers/xp/reference-data-service/introduction/.