Skip to main content

Class Types

Every class has a type that determines its role in the data model. There are four types: BASIC, ABSTRACT, NONE, and EXTENSION.

The core rule

Every object in the system has exactly one BASIC class. This is the object's primary identity. On top of that, objects accumulate any number of ABSTRACT classes — through inheritance and through relations.

BASIC

A BASIC class is an instantiable type. When a user creates an object, they choose a BASIC class. The object then has that BASIC class as its primary type, plus all ABSTRACT classes inherited through the inheritance chain.

{
"class": "commons.person",
"type": "BASIC",
"inherits": "commons.entity"
}

A person object has the types: commons.person (BASIC), commons.entity (ABSTRACT), commons.item (ABSTRACT).

ABSTRACT

An ABSTRACT class cannot be the sole type of an object. Objects accumulate ABSTRACT classes in two ways:

1. Through inheritance — every class in the inheritance chain is added to the object's type set.

commons.person (BASIC) → commons.entity (ABSTRACT) → commons.item (ABSTRACT)

A commons.person object has all three types.

2. Through relations — when an object is linked as a relation, the type defined on that relation is added to the linked object.

See Relations for details.

NONE

NONE is used exclusively in index.json. It marks the entry point of a repository and cannot be used for objects.

{
"class": "my-project.index",
"type": "NONE",
"imports": [
"my-project.employee",
"my-project.company"
]
}

EXTENSION

An EXTENSION class adds value types, relations, or formulas to an existing commons class without replacing it. Use extends instead of inherits.

{
"class": "my-project.person",
"type": "EXTENSION",
"extends": "commons.person",
"valueTypes": [
{ "name": "employeeNumber" }
]
}

This adds an employeeNumber field to every commons.person object in the tenant, without creating a new class.


Supported properties per type

The table below shows which top-level properties are supported for each class type.

PropertyBASICABSTRACTEXTENSIONNONE
valueTypes
relations
formulas
scriptsGENERAL only
lists
views
layouts
assets
translations
data
migration

Notes:

  • Scripts on NONE must have scope GENERALOBJECT-scoped scripts require a concrete object and are not supported.
  • lists can be defined on any class type and referenced from any class in the same namespace. Define them where they are logically grouped, regardless of class type.
  • data and migration are most commonly placed on a dedicated NONE-typed class, but are technically valid on any type.

Required properties per type

Typeinheritsextends
BASICrequirednot used
ABSTRACTrequirednot used
NONEnot applicablenot used
EXTENSIONnot usedrequired

BASIC and ABSTRACT classes must always declare inherits. The only class without inherits is commons.item itself — it is the root of the entire hierarchy and is provided by the platform.


Inheritance rules

  • Inheritance chains must not contain loops
  • A BASIC class must never inherit from another BASIC class, directly or indirectly. Every class in a BASIC class's inheritance chain must be ABSTRACT.
  • commons.item is the root of all classes — every class eventually inherits from it
  • EXTENSION classes use extends (not inherits) and reference a class they are extending

EXTENSION vs custom ABSTRACT: when to use which

Both can add fields to an existing commons class, but they behave very differently.

EXTENSION adds fields to every object of the target commons type in the entire tenant:

{
"class": "my-project.person",
"type": "EXTENSION",
"extends": "commons.person",
"valueTypes": [
{ "name": "employeeNumber" },
{ "name": "department" }
]
}

Every commons.person in the tenant now has employeeNumber and department, regardless of their role.

Custom ABSTRACT adds fields only to objects that take on a specific role via a relation:

{
"class": "my-project.agent",
"type": "ABSTRACT",
"inherits": "commons.person",
"valueTypes": [
{ "name": "agentCode" },
{ "name": "licenseNumber" }
]
}

agentCode and licenseNumber are only added to a person when they are linked as a my-project.agent relation. Other persons are unaffected.

Decision guide:

QuestionUse
Should every person / company in the tenant have this field?EXTENSION
Should only persons / companies in a specific role have this field?Custom ABSTRACT
Do you want to use this type as a relation target?Custom ABSTRACT

Example: EmploymentAgreement

This example shows how a single object can accumulate multiple types.

{
"class": "commons.employmentAgreement",
"type": "BASIC",
"inherits": "commons.agreement",
"relations": [
{ "type": "commons.company", "required": true, "multiple": false },
{ "type": "commons.employee", "required": true, "multiple": false }
]
}
{
"class": "commons.employee",
"type": "ABSTRACT",
"inherits": "commons.person"
}

When a commons.person object is linked to an employmentAgreement as the commons.employee relation:

  • The person's existing types: commons.person, commons.entity, commons.item
  • The relation adds: commons.employee
  • The person now has all four types

The person's BASIC class remains commons.person. The commons.employee class adds its own value types (e.g., job title, employee number) to the person, but does not change what kind of object it fundamentally is.


Example: complete ABSTRACT class definition

An ABSTRACT class can carry the same properties as a BASIC class — valueTypes, relations, lists, translations, and more. Everything defined on the ABSTRACT class is inherited by any BASIC class that extends it.

This example defines my-project.serviceTicket as an ABSTRACT base for ticket types. Concrete ticket classes (e.g. my-project.bugReport, my-project.changeRequest) inherit from it.

{
"class": "my-project.serviceTicket",
"type": "ABSTRACT",
"inherits": "commons.dossier",

"valueTypes": [
{ "name": "subject", "required": true },
{ "name": "priority", "dataType": "LIST", "list": "my-project.ticketPriority" },
{ "name": "description", "dataType": "TEXTBLOCK" }
],

"formulas": [
{ "valueType": "name", "template": "{{subject}}" }
],

"relations": [
{ "type": "commons.person", "required": true, "multiple": false }
],

"lists": [
{
"name": "my-project.ticketPriority",
"items": [
{ "key": "low", "color": "#1a7f37", "backgroundColor": "#dafbe1" },
{ "key": "medium", "color": "#9a6700", "backgroundColor": "#fff8c5" },
{ "key": "high", "color": "#cf222e", "backgroundColor": "#ffebe9" }
]
}
],

"translations": [
{
"language": "en",
"singular": "Service Ticket",
"plural": "Service Tickets",
"valueTypeLabels": {
"subject": "Subject",
"priority": "Priority",
"description": "Description"
},
"listValueLabels": {
"my-project.ticketPriority": {
"low": "Low",
"medium": "Medium",
"high": "High"
}
}
}
]
}

The ticketPriority list uses both color and backgroundColor so each value renders as a colored badge — appropriate here because priority carries visual meaning. For lists where the values are neutral (e.g. categories, departments), omit the color properties entirely. See Lists for the full rendering rules.

A concrete subclass only needs to add what is specific to it:

{
"class": "my-project.bugReport",
"type": "BASIC",
"inherits": "my-project.serviceTicket",

"valueTypes": [
{ "name": "stepsToReproduce", "dataType": "TEXTBLOCK" }
],

"translations": [
{
"language": "en",
"singular": "Bug Report",
"plural": "Bug Reports",
"valueTypeLabels": {
"stepsToReproduce": "Steps to reproduce"
}
}
]
}

my-project.bugReport inherits subject, priority, description, the ticketPriority list, the commons.person relation, and all translations from my-project.serviceTicket. It only needs to define what is unique to a bug report.


Example: Shareholder (abstract relation type)

A shareholder relation needs to accept both persons and companies. Since you cannot add a BASIC class as a relation type (that would restrict it to only that type), you use an ABSTRACT class that sits above both.

{
"class": "my-project.shareholder",
"type": "ABSTRACT",
"inherits": "commons.entity"
}

commons.entity is inherited by both commons.person and commons.company. So when the platform needs to find objects that can be linked as a my-project.shareholder:

  1. It walks up the inheritance chain of my-project.shareholder
  2. It finds commons.entity as the most specific type that BASIC classes inherit from
  3. It searches for objects of type commons.entity (both persons and companies)

When creating a new shareholder, the user is presented with a choice: create a commons.person or a commons.company. The newly created object gets the my-project.shareholder class added automatically.