Skip to content

Concepts, Assets, Participants, Transactions and Events (v1)

Dan Selman edited this page Mar 22, 2021 · 1 revision

Concepts, Assets, Participants, Transactions and Events

Concept

concerto.Concept is the root of the type hierarchy: everything ultimately extends concerto.Concept.

All namespaces implicitly import all the types in the concerto namespace. I.e. import concerto.*

A concept does not have to have an identifier. Identifiers are simple String fields used as the natural idenfifier for types. Concerto does not support compound identifiers.

A Concept may include collections of other concepts (belongings, or contained/inline instances):

concept Person {
   o Concept[] myStuff
}

Relationships are used to define typed pointers to external identified instances.

A concept may not include references to Concept - as the base concerto.Concept is not identified by a field.

concept Person {
   --> Concept[] otherStuff // invalid
}

A concept may have an implicit (system) identifier:

namespace test

concept Person identified { // creates the system $identifier field
   o String name 
}

Serialized as:

{
    "$class" : "test.Person",
    "$identifier" : "DAN",
    "name" : "Dan Selman"
}

A concept may alternatively have an explicit (named) identifier:

concept Person identified by ssn {
    o String ssn
}

Serialized as:

{
    "$class" : "test.Person",
    "name" : "Dan Selman",
    "ssn" : "xx-xxxx-xx"
}

A concept may include references to any identified types:

concept Employee {
    o String name
    --> Person manager // this is valid
}

Identifiers and the Type Hierarchy

To determine whether a type has an identifying field Concerto walks up the type hierarchy until it finds a type with an identifying field (either named or system), until it reaches concerto.Concept.

Within a hierarchy of identified types, a given type may redefine its identifying field.

Do we want to support ^^^^? It may make the code more complex, as to determine whether a field is identifying for a type you need to look at not just at the type declaration, but at the type of the instance.

namespace test

concept Stuff identifier {
    o String name
}

concept Vehicle identified by vin extends Stuff {
    o String vin
    o String make
    o String model
}

/**
 * Truck may override the identifying field defined by a super type ...
 */
concept Truck identified by truckId extends Vehicle {
    o String truckId // note that Trucks must still have a vin, however it is no longer identifying
}

concept Tollbooth {
    --> Vehicle[] vehicles // could be a relationship to either Vehicle or Truck
}

Serialization of relationships persist both the type of the relationship as well as the identifier:

{
    "$class" : "test.Tollbooth",
    "vehicles" : [
        "test.Vehicle#123",
        "test.Truck#ABC"
    ]
}

Asset & Participant

Assets are used within models to denote identifiable Things (nouns), while Partipants are identifiable people/organizations/parties.

Both concerto.Asset and concerto.Participant extend concerto.Concept.

namespace concerto

abstract concept Concept {}

abstract concept Asset identified extends Concept {
}

abstract concept Participant identified extends Concept {
}

The keyword asset is semantic sugar to denote that a type extends concerto.Asset:

asset Truck identified by vin {
    o String vin
    o String make
    o String model
}

Is equivalent to:

concept Truck extends concerto.Asset identified by vin {
    o String vin
    o String make
    o String model
}

The keyword participant is semantic sugar to denote that a type extends concerto.Participant:

participant Party identified by email {
    o String email
}

Concerto enforces that types that are declared as asset or participant must ultimately extend concerto.Asset or concerto.Participant respectively. This ensures that the modeller can denote the role of a type when declaring it, and document/enforce its ultimate super type.

Not clear that we want to enforce this ^^^^ restriction?

E.g.

asset Vehicle {
    o String make
}

participant Truck extends Vehicle { // this is invalid, cannot redeclare an asset as a participant
}

Transaction and Event

Transactions are used within models to denote that an action should be performed. For example, UpdateAddressDetails.

Events are used within models to denote that something has happened. For example, AddressModified.

E.g. in Hyperledger Fabric there is a very different mechanism for handling incoming transactions (and responses to transactions), to the enqueue of events onto the Fabric event bus. Similarly Cicero supports a syncronous request/response programming model based on transactions, while also supporting async emission of events.

Both Transaction and Event must have a timestamp fields that captures a DateTime of occurrence.

Both concerto.Transaction and concerto.Event extend concerto.Concept.

The keyword transaction is semantic sugar to denote that a type extends concerto.Transaction:

The type concerto.Transaction is defined as:

abstract concept Transaction {
    o DateTime timestamp
}

The keyword event is semantic sugar to denote that a type extends concerto.Event:

The type concerto.Event is defined as:

abstract concept Event {
    o DateTime timestamp
}