Relations
Relations connect objects to other objects. When an object is linked through a relation, it receives the relation's type as an additional class — along with all the value types and behaviors that class defines.
Definition
{
"type": "commons.employee",
"required": true,
"multiple": false
}
| Property | Type | Default | Description |
|---|---|---|---|
type | TypeFQN | — | The class that will be added to the linked object. |
required | boolean | false | If true, the relation must be filled in. |
multiple | boolean | false | If true, multiple objects can be linked. |
cascadeDelete | boolean | false | If true, deleting this object also deletes all linked objects. |
commonRelation | TypeFQN | — | Limits selectable objects to those already linked to the same object of this type as the current object. |
inherit | TypeFQN | — | Enables hierarchical filtering by walking a parent chain on the target type. Points to the type of that parent relation (usually a self-reference). |
inheritTwoWay | boolean | false | When true, hierarchical filtering traverses the chain in both directions. |
defaultValue | TypeFQN | — | The object to link by default when no value has been set. |
How relations add classes
When an object is linked through a relation, the linked object receives the relation's type as an additional class. This adds all value types and formulas of that class to the linked object — without changing the object's BASIC class.
Example:
{
"class": "commons.employmentAgreement",
"type": "BASIC",
"inherits": "commons.agreement",
"relations": [
{ "type": "commons.employee", "required": true, "multiple": false },
{ "type": "commons.company", "required": true, "multiple": false }
]
}
{
"class": "commons.employee",
"type": "ABSTRACT",
"inherits": "commons.person"
}
When a commons.person is linked as the employee on an employmentAgreement:
- The person retains its BASIC class:
commons.person - It gains the class
commons.employee - It can now store fields defined on
commons.employee(e.g., employee number, start date)
Searching for linkable objects
When linking a relation, the platform needs to know which objects are eligible. It does this by walking up the inheritance chain of the relation's type and finding the most specific type that is inherited by at least one BASIC class.
Example with ABSTRACT relation type:
{
"class": "my-project.shareholder",
"type": "ABSTRACT",
"inherits": "commons.entity"
}
The relation is of type my-project.shareholder. Walking up:
my-project.shareholder(ABSTRACT, no BASIC class inherits this directly)commons.entity(ABSTRACT, inherited bycommons.personandcommons.company)
The platform searches for objects of type commons.entity — meaning both persons and companies appear in the search results.
Creating new objects through a relation
If no existing object fits, the user can create a new one directly from the relation. The platform looks for all BASIC classes that inherit the searchable type (commons.entity in the example above) and presents them as options: commons.person or commons.company.
The newly created object automatically receives the relation type (my-project.shareholder) as an additional class.
Cascade delete
When cascadeDelete is true, deleting an object also deletes all objects linked through that relation.
{
"type": "my-project.invoiceLine",
"multiple": true,
"cascadeDelete": true
}
Use this for objects that have no meaningful existence without their parent — for example, invoice lines that belong to a single invoice.
Hierarchical filtering
inherit enables filtering through a parent chain on the target type. It points to the TypeFQN of the parent relation on the target type — typically a self-referential relation that forms a hierarchy.
Example: commons.territory has a superTerritory relation pointing back to commons.territory, forming a tree:
Amsterdam → North Holland → Netherlands → Europe → World
A commons.company has a region relation to commons.territory. Setting inherit to commons.territory on that relation tells the platform to discover the superTerritory chain and walk it when filtering:
{
"type": "commons.territory",
"inherit": "commons.territory"
}
A company linked to Amsterdam can now be found by filtering on North Holland, Netherlands, or any ancestor — even though it is only directly linked to Amsterdam.
Discovery rule: the platform looks for a relation on the target type (commons.territory) whose own type equals the inherit value (commons.territory). That self-referential relation defines the chain to walk.
One-way vs two-way
inheritTwoWay: false (default) — only ancestors of the stored value match. Filtering on a general territory finds all companies in any sub-territory:
| Company linked to | Filter on Netherlands | Filter on Amsterdam |
|---|---|---|
| Amsterdam | ✓ (Amsterdam is under Netherlands) | ✓ |
| North Holland | ✓ (North Holland is under Netherlands) | — |
| Netherlands | ✓ | — |
inheritTwoWay: true — descendants also match. A company linked to Netherlands is also found when filtering on Amsterdam:
| Company linked to | Filter on Netherlands | Filter on Amsterdam |
|---|---|---|
| Amsterdam | ✓ | ✓ |
| Netherlands | ✓ | ✓ (Netherlands is above Amsterdam) |
Use inheritTwoWay when you want filtering to work in both directions, for example in a territory-based access model where users scoped to a sub-territory should still see objects linked to a parent territory.