Entity


Everything in a Hiperspace is either entity, describes an entity or projects an entity with a different view. An entity is a durable data fact with optional values and/or extensions much like a table in a database, a document in a document store or a node (with properties) in a graph with connected edges.

The term 'entity' is used to distinguish it from other 'types' in Hiperspace, and because it provides declarative functionality that is not implemented in other language definitions

  • Table : It provides definition of the type, views (that it projected to too) , joins (to other {Entity, Segment, Aspect}), calculated (but not stored) properties, and indexes.
  • Class : It distinguishes between keys, values and extents (properties that are not stored), and logic for encoding/decoding to storage.

All sections are optional in a HiLang definition except the type name, but entities must have keys (entity Instrument (Id : Int32); is a valid definition).

Calculations in an entity value are much like the default values in a relational definition, except that they can use externally defined functions and reference other values. Once calculated has been evaluated it is stored in Hiperspace and not evaluated again (unless the null value is assigned). NB the examples include Valid as a value to allow validation to be overwritten - need to clear the value for update.

Calculations in an extension are evaluated when needed, and never stored with the object. These extensions make an entity behave much like a row in a spreadsheet table.


Entity definition

In the language of HiLang, entity is a base-type with Inheritance declared with a ":" following the name. Unlike a class declaration, equivalence is declared with '=' to the view.

The declaration `view Edge2 : Edge = Edge();' describes a view Edge2 that is a Edge and equivalent to an Edge. The view is a common addition to HiLang schema models to allow an entity to provide the Edge view twice.

entity ERP.Products.Product : Versioned
    = Node  (Name = Name, SKey = SKey, TypeName = "Product")

ERP.Products.Product is a Versioned and equivalent to Node

entity ERP.Client.Customer : Versioned
    = Node  (Name = Name, SKey = SKey, TypeName = "Customer"),
      Edge  (From = this, To = BillTo, TypeName = "BillTo"),
      Edge2 (From = this, To = ShipTo, TypeName = "ShipTo")
(
    Id              : Int32
)
{
    Name            : String,
    BillTo          : ERP.Location.Address,
    ShipTo          : ERP.Location.Address,
}

ERP.Client.Customer is a Versioned and equivalent to Node and two Edge, to expose the BillTo and ShipTo references as graph references.

entity ERP.Sales.Order : Versioned
    = Node  (Name = "", SKey = SKey, TypeName = "Order"),
      Edge  (From = this,        To = Customer,    TypeName = "Order-Customer"),
      Edge2 (From = Customer,    To = this,        TypeName = "Customer-Order"),
      Edge3 (From = this,        To = Store,       TypeName = "Order-Store"),
      Edge4 (From = Store,       To = this,        TypeName = "Store-Order"),
      Edge5 (From = this,        To = Salesperson, TypeName = "Order-Sales"),
      Edge6 (From = Salesperson, To = this,        TypeName = "Sales-Order")
(
    OrderNumber     : Int32
)
{
    Customer        : ERP.Client.Customer,
    Store           : ERP.Location.Store,
    Salesperson     : ERP.Sales.Salesperson,
...;

ERP.Sales.Order is a Versioned and equivalent to Node and six Edge to provide a graph view of an order with navigation (as a graph) to and from the referenced {Customer, Store, Salesperson}.


Keys

An entity can have any number of key parts, and those keys can be references to other entities and complex value types

entity ERP.Finance.Tarriff : Versioned
    = Node  (Name = From.Name + " to " + To.Name, SKey = SKey, TypeName = "Tarriff"),
      Edge  (From = From, To = To, TypeName = "Tarriff")
(
    From            : ERP.Location.Country,
    To              : ERP.Location.Country
)
...;

ERP.Finance.Tarriff is identified by From Country and To Country. Worth noting that the Tarriff defines an Edge between two countries - a graph view could be viewed as UK -> USA (Tarriff) without the ontological model including Tarriff as an intermediate node.

ERP.Location.Country is defined as an entity, when referenced in a another entity it is not added as a value, but a reference with navigation and as needed loading. The entity exposes a Country as a key property, but the KeyType structure includes a KeyRef<ERP.Client.Customer.KeyType, ERP.Client.Customer> that includes the Customer's KeyType and logic to load the full Country if needed.


Values

Each value can be a Field-type (like a key) or a calculation that is either provided by the application or derived prior to binding to a SubSpace.

entity ERP.Sales.Order : Versioned
...
(
    OrderNumber     : Int32
)
{
    Customer        : ERP.Client.Customer,
    Store           : ERP.Location.Store,
    Salesperson     : ERP.Sales.Salesperson,

    "all the order lines included in the order"
    Lines           : Set <ERP.Sales.OrderLine>,
    Valid           = Customer = null || changed(Customer) = true ||
                      Store = null || _changeStore = true ||
                      count(Lines.LineNumber) = 0 || _changeLines = true
                      ? false : true
}

ERP.Sales.Order has references to {Customer, Store, Salesperson} (since they are declared as entities). ERP.Sales.Order has has a Set of ERP.Sales.OrderLine. Set is a unique collection (keyed by its own key), where a List could contains duplicates. Collections must be value types.

ERP.Sales.Order has a Valid value property that is calculated using the expression if it does not have a value. In this expression changed is a function that was declared with %function (unary, changed, ERP.ERPFunctions.Changed, Boolean); and implemented externally. The Valid property is normally associated with Horizon filter like the one in ERPSpace example.

A Set of entity referenced can be added like Approval : Set<Ref<Access.RBAC.Group>> (in RBAC example) by using a utility value like

/* Helper to enable a set of references */
value Ref<T>
{
    Value       : T
};

Extents

Where a value can have a reference to an entity, extents references are either a reference segments , aspects, or a join to a set of entititied by by query. Tariff : ERP.Finance.Tarriff (From = Store.Country, To = Customer.BillTo.Country) is a join to ERP.Finance.Tarriff matching the To and From members to the Store and BillTo contries.

Invoice : ERP.Sales.Invoice can either be a single reference to an aspect or a set of segment. In this example it is a single reference to a ERP.Sales.Invoice aspect (one, optional)

    Tariff          : ERP.Finance.Tarriff (From = Store.Country, To = Customer.BillTo.Country),
    Shipping        : ERP.Finance.Shipping (From = Store.Country, To = Customer.ShipTo.Country),
    Invoice         : ERP.Sales.Invoice,

    Total           = sum (Lines.Price),
    ShippingCost    = Shipping = null ? sum (Lines.Shipping) : sum (Lines.Shipping) + max (Shipping.Fee),
    SalesTax        = sum (Lines.Tax),
    TariffTax       = Tariff = null ? 0.0 : max(Tariff.Tax.Rate) * Total,
    Bill            = Total + ShippingCost + SalesTax + TariffTax,
    Cost            = sum (Lines.Cost),
    Commission      = Salesperson = null ? 0.0 : Profit * Salesperson.CommissionRate,
    Profit          = 0.0 + Total - Cost - Commission

Extents consume no space within the entity and evaluated when needed. Adding @Once to a calculation causes the value to be cached.

In the Invoice example we've chosen to implement OrderLine as a Set of values, but could have implemented it as a segment with similar Behavior - the choice was made because we don't need to join Product to OrderLine, and avoids the need to add a validation horizon to order line to ensure that lines are not added after an Invoice has been created.


Other considerations

Hash

Hash assignment is optional for entity definitions because they will be generated as needed, but the order of statements within a HiLang schema model will determine the number assigned. Careful use of hash number assignment will avoid the need to remap a durable Hiperspace and provide forward compatibility.

Hash assignment is also important for compatibility with protobuf definitions that the source of a Hiperspace schema model.

entity ERP.Location.Address : Versioned
    = Node  (Name = "{ Street: '" + Street + "', PostalCode: '" + PostalCode + "'}", TypeName = "Address", SKey = SKey),
      Edge  (From = this, To = Country, TypeName = "Address-Country")       # 42
(
    Id              : Int32                                                 # 1
)
{
    Street          : String                                                # 2,
    Town            : String                                                # 3,
    City            : String                                                # 4,
    County          : String                                                # 5,
    Country         : ERP.Location.Country                                  # 6,
    PostalCode      : String                                                # 7,
    Valid           = Country = null || PostalCode = null ? false : true    # 8
};

Documentation

It’s a good idea to include documentation for an entity since this information is generated into code where it can be used (by IntelliSense) to inform the developer of the meaning an entity or member and for automated documentation tools. It is also passed to UML modelling tools using the XMI export/import function.

Documentation is distinct from /* comments */ that are just for the HiLang modeler, and not generated.

Name

The Name of an entity cannot have type parameters, since it must unique within the domain, but can be the same in different namespaces. entity ERP.Sales.Order and entity ERP.Store.Order are still unique, but since the Set in the DomainSpace must havea unique name, they would be referenced from the domain space as SalesOrders and StoreOrders. if there is a class on the in the final part.

References to entities are always full qualified to avoid the accidental mutation that is common with other (programming) languages that use namespace for resolution.

Inheritance

Inheritance in HiLang can be thought of as automatic-copy of the definition of another element to avoid the duplication of definitions. Entities can inherit from multiple base types (as long as names don't clash).

Versioned in entity ERP.Location.Address : Versioned is a good example of inheritance since @Versioned type Versioned { "Flag for read horizon filter to hide when true" Deleted = false }; combines the @Versioned property (used to add versioning support to generated code) and the common convention of a Deleted flag to hide an object from the current (query) view while still available to historical views.


Copyright © Cepheis 2024