Skip to content

Actor Extensions

Actor Extensions is an umbrella term for patterns used by some actors to allow for customizations and extensions of their default behavior. At the heart of this sits the Actor Type Definitions that allows some actors to be typed. A typed actor is an instance of a regular actor, with an additional custom type. The type allows the actor to take on new roles and model data more accurately.

Hantera’s standard Actors does a good job at modeling business objects in general. Being an OMS, the Order Actor is a bit opinionated around how to model orders. Other Actors are less opinionated, but comes with less functionality over-all. Examples are the Customer and Ticket Actors. With Actor Extensions, we can define different types of Tickets for different purposes. For example, a RMA Ticket and a support Ticket may look vastly different, while still benefiting from the Tickets ability to model long-term transactions and completion rules.

An Actor type must be defined before being used. This is done through the registry, and detailed below. Each type definition can define a number of Items and Relations. Types are optional. When creating an Actor that supports extensions, a typeKey can be provided to specify the type. If no specified, the actor will function without extensions as well.

Items

An Actor Item is an arbitrary entity that is named by the type definition. A typed Actor may have many instances of the same type of Item. It may also have many different types of Items. Items can have Relations just like a typed Actor. All Items must have a type. For example, a Ticket without a specified type can not contain any Item, as there’s no type of item defined.

Relations

A type can define Relations to any other type of node in the Graph. Each Relation will manifest as a Graph bi-directional edge to related nodes in the Graph. This means that a typed Actor’s related Graph node can contain Edges not normally supported by the Actor. Items can also define Relations, allowing for complex and accurate modeling of business data.

While the Actor’s main Graph node automatically generates Edges for the Actor’s typed Items, Relations could be added between types of Items as well. This is shown in the example below.

Creating Items and Relations

Actors that supports extensions are data-focused Actors, and data-focused Actors supports Commands for mutating it’s state. The Commands to work with Items and Relations are the same across all actors to simplify.

The Commands are as follows (links are provided to the Ticket version of these commands, but they should be identical with other Actors):

Graph

As mentioned above, Items and Relations are represented as nodes and edges in the Graph. This is mostly done conventionally based on the Type and Relation keys.

For example, a Ticket type defined with shipment key will generate a set called tickets:shipment in the graph. Additionally, if we defined an Item called package to the type, there would be a ticketItems:shipment.package root set, and a items:package on the tickets:shipment node.

Relations are similarly named. For example, let’s say our shipment type has an order relation named orders, the tickets:shipment nodes will have an edge called related:orders.

In a more concise way:

EntityConvention
Actor nodes<base set>:<typeKey>
Item nodes<base set>:<typeKey>.<itemTypeKey>
Relation edgesrelated:<relationKey>
Inverse relation edges<base set>:<typeKey>[.<itemTypeKey>]

To explore the dynamic Graph, you can use the Query Editor in the Portal, or use the Graph Meta endpoint available at GET https://<hostname>/resources/graph

Access Control

Access control of custom nodes are based on the base node and refined using ABAC. The attribute is typeKey and an identity needs a matching access attribute.

For example, an identity with attribute-limited ACE typeKey@graph/ticket:query can only query tickets where it has a matching value for the typeKey access attribute.

Type Definitions

Actor Type definitions are set up in the Registry. The path for the definitions depends on the Actor that is being extended. The convention looks like this: actors/<actor>/types/<typeKey>

The value is an object containing the definition of the type.

Type Definition Schema
defaultNumberPrefixstring (optional)

For Actors that have auto-increment natural keys (most data-focused Actors), this determines the default number prefix to use. If not specified, the Actor’s default prefix will be used.

This is useful when different types should have different number series.

itemsmap of string to Item Definition Schema (optional)

The Item types that are allowed on this Type.

relationsmap of string to Relation Schema (optional)

The Relations that are allowed on this Type.

Item Definition Schema
relationsmap of string to Relation Schema (optional)

Zero or more Relations Schemas

Relation Definition Schema
nodestring

The name of the Node allowed in this Relation.

cardinality’single’ or ‘many’

Whether the relation allows one or more relations. This will be enforced by the Actor upon creating Relations.

inverseNamestring (optional)

An additional identifier to add to the inverse edge. Optional if there’s only one Relation between the same nodes, otherwise required for all but one Relation.

Errors

The Registry doesn’t enforce schema upon writing. This is true across the whole Registry. Instead, we can rely on Signals to make sure our definitions are correct.

Example: Modeling a Shipment

Let’s say you want to model a Shipment as a Delivery is being shipped to a customer. A Shipment has a limited lifespan, so this makes Ticket Actor a very good option for this.

In our example, a shipment will have an Order and Delivery relation, as well as containing packages. We will also store events for each package as well as Shipment-specific events (common for all packages). To model this, we could apply the following manifest:

uri: /registry/actors/ticket/types/shipment
spec:
value:
defaultNumberPrefix: "SHIP"
items:
event:
relations:
package:
node: 'ticketItem:shipment.package'
cardinality: single
package: {}
relations:
delivery:
node: delivery
cardinality: single

As the shipment.event.package relation is optional, an event without a package will be regarded as a Shipment event. Since relations are bi-directional in the Graph, the package nodes in the graph will automatically receive a ticketItems:shipment.event edge. This is why we can leave the package item definition empty.

It’s important to note that while Relations are bi-directional in the Graph, the Relation is controlled by the entity that defines it. So we can’t add an event to a package in the above case. Instead we must add the package to the event.