Basic concepts grammar

To use DSL you must know its grammar. For now, since there is no IDE to navigate you through the available concepts, online documentation is required for writing correct DSL. In grammar definition, keywords are written in uppercase and identifications are written lowercase.

Fortunately basic grammar is very familiar and can be grasped with couple of examples.

Note: this is not a complete grammar definition for these concepts.

Module:

Parent: none

Grammar: MODULE name

Example:

module MyDomain;

Description:

Module is used to group various objects under single name. It maps to namespace/schema/package in various languages/database.

Aggregate root:

Parent: Module

Grammar: (AGGREGATE ROOT? | ROOT) name ('(' key (',' key)* ')')?

Alternative grammar (BIG | GUID | UUID) (AGGREGATE ROOT? | ROOT) name

Example:

module App
{
    root MyData; //convenience for declaring root with ID property which is a sequence and a primary key
    aggregate root Person(SSN); //declaring root with explicit primary key
    big aggregate Log; //sequence primary key is an long instead of int type
}

Description:

Aggregate root is collection of objects bound together under entity. Usually it represents a single table, but can be used like document or similar data structure. Since every aggregate is also an entity, it has a unique identification represented by its URI.

Value:

Parent: Module

Grammar: VALUE name

Example:

module AddressBook
{
    value Phone;
}

Description:

Value is an object without identity, defined by its attributes, that can be used to store data embedded in its parent.

Entity:

Parent: Module

Grammar: ENTITY name ('(' key (',' key)* ')')?

Example:

module Tables
{
    entity MyTable; //entity declared as this is called a weak entity because it doesn't have primary key
}

Description:

Entity is an object with identity and a basic building block in entity-relationship model. Every entity has a unique identification represented by its URI.

Simple property:

Parent: Aggregate root, Entity, Value, Specification, Event and many more

Grammar: (INT | LONG | STRING | DATE | MAP | DECIMAL | TIMESTAMP | ...) '[]'? '?'? name Grammar: (LIST | ARRAY | SET) < (INT | LONG | STRING | DATE | MAP | DECIMAL | TIMESTAMP | ...) '?'? > '?'? name

Example

module AddressBook
{
    root Person
    {
        string Name; //string property which can be of arbitrary length
        date? Birthday; //optional date property
        string[] Phones; //an array of strings
    }
}

Description:

Simple properties are used for basic and useful built in data types. Property can be optional (nullable) or an array. Elements in array can't be null.

Specification:

Parent: Aggregate root, Entity, Value, Snowflake and many more

Grammar SPECIFICATION name EXPRESSION

Example:

module Data
{
    root Person
    {
        string Name;
        specification FindByName 'it => it.Name.StartsWith(Search)'
        {
            string Search; //specification can have simple and reference properties
        }
    }
}

Description:

Specification is a predicate which states a condition. It can be used for defining search conditions, validations and in many other parts of the system.

Reference property:

Parent: Aggregate root, Entity, Value, Specification, Event and many more

Grammar: (module '.')? reference '[]'? '?'? name Grammar: (LIST | SET | ARRAY) < (module '.')? reference '?'? > '?'? name

Note: when referencing aggregate root inside entity or aggregate root, * is mandatory as a pointer to aggregate root

Example:

module AddressBook
{
    value Phone
    {
        string? Region;
        string Number;
    }

    root Person
    {
        string Name;
        Phone ContactNumber;
        AddressBook.Phone? Mobile; //when we are referencing object from another module, we must use full path
        Phone[] AlternativeNumbers;
        Person? *RecommendedBy; //since Person is an aggregate root, we must reference it with star

        specification FindByPhone 'p => Numbers.Contains(p.ContactNumber) || p.Mobile != null && Numbers.Contains(p.Mobile.Value)'
        {
            Set<Phone> Numbers;
        }
    }
}

Description:

Reference property is used to access an object defined in DSL. It can be optional (nullable) or an array. Elements in array can't be null.

Snowflake:

Parent: Module

Grammar: SNOWFLAKE < (module '.')? entity > name

Example:

module AddressBook
{
    root PersonRoot
    {
        string Name;
        Phone ContactNumber;
        Person? *RecommendedBy;
    }

    snowflake<PersonRoot> Person; //snowflake starts from a single PersonRoot which means it shares it's URI
}

Description:

View of an entity. View can include any information accessible through relations from this entity.

Snowflake property

Parent: Snowflake

Grammar: name ('.' name)* (AS? alias)?

Example:

module DataStructure
{
    root LinkedList
    {
        int Number;
        LinkedList? *Next; //pointer to the next item in a list
    }
    snowflake UnrolledList LinkedList
    {
        Number; //if alias is not declared, name of the used property will be used as alias
        Next.Number FirstNumber; //we can navigate through reference properties
        Next.Next.Number as SecondNumber;
    }
}

Description:

Snowflake property is declared as a path to another property. Path can be navigated through relationships.

Enum:

Parent: Module

Grammar: ENUM name

Example:

module AddressBook
{
    enum PhoneType;
}

Description:

Enum is familiar concept from other languages, used to define fixed set of types.

Enum label

Parent: Enum

Grammar: name

Example:

module AddressBook
{
    enum PhoneType
    {
        Mobile;
        Home;
        Work;
    }
}

Description:

Labels are used for defining enum types.