More concepts grammar

Concepts in DSL are used to declare usage of some feature, which can be small as a property on an object or as complex as an index with specification which needs a lot of infrastructure to run.

Main idea behind DSL is that usually business applications consists from model which some try to describe in UML, most don't even bother and go straight to code, but some like us like to explore and play with model in DSL before we settle on something that feels right and can be easily evolved in the future.

While basic building blocks of Domain-Driven Design can get us far, soon we must go outside of it to declare rest of the domain and some common patterns of our application.

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

Sequence:

Parent: Simple property

Grammar: SEQUENCE

Example:

module Domain
{
    root Person(ID)
    {
        int ID { sequence; } // sequence can be declared on int, long and guid, date, timestamp and some other property types
    }
    //there is convenient DSL for this pattern: root Person;
}

Description:

Sequence is used to assign next property value by server at insertion time. It's similar to serial, auto_increment concepts found in other databases.

Explicit reference:

Parent: Aggregate root, entity

Grammar: (module '.') reference '(' key (',' key)* ')' '*' name

Example:

module Park
{
    root Vehicle
    {
        date Bought;
        int Year;
        string ModelName;
        Model(Year, ModelName) *Model; //when referencing composite entity, all properties must be declared and specified
    }
    root Model(Year, Name)
    {
        int Year;
        string Name;
        string Description;
    }
}

Description:

Relationship to another aggregate using explicitly defined properties.

Simple reference:

Parent: Aggregate root, entity

Grammar: (module '.') reference '?'? '*' name

Example:

module Store
{
    root Product
    {
        string Name;
        money Price;
        Group? *Group; //simple reference is an shortcut for declaring property GroupID of correct type and using explicit reference syntax
    }
    root Group
    {
        string Name;
    }
}

Description:

Relationship to another aggregate using implicitly defined property nameID. Relationship can be made only to aggregate with single primary key.

Collection reference:

Parent: Aggregate root, entity

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

Example:

module Store
{
    root Product
    {
        string Name;
        money Price;
        Set<Group>? *Groups; //collection reference is used for n-n mapping
    }
    root Group
    {
        string Name;
    }
}

Description:

Multiple relationship with another aggregates. Used to implement many-many mapping.

Snowflake order:

Parent: Snowflake

Grammar: ORDER BY key (ASC | DESC)? (',' key (ASC | DESC)? )*

Example:

module Store
{
    root Product
    {
        string Name;
        money Price;
    }
    snowflake<Product> ProductList
    {
        Name;
        Price;
        order by Price desc, Name;
    }
}

Description:

Order for results found in snowflake. This order usually has business meaning.

Details:

Parent: Aggregate root, entity

Grammar: DETAIL < (module '.') reference '.' property > name

Example:

module Store
{
    root Product
    {
        string Name;
        money Price;
        Group? *Group;
    }
    root Group
    {
        string Name;
        detail<Product.Group> Products; //detail to another aggregate root is read only property
    }
}

Description:

Related aggregate objects can be included inside another entity when there is a relationship between them.

Index and Unique index:

Parent: Property in aggregate root or entity

Grammar: INDEX and UNIQUE

Example:

module Store
{
    root Product
    {
        string Name { unique; }
        money Price;
    }
}

Description:

Index will provide fast access to data. Unique index can be used if data must be unique.

Index and Unique index with specifications:

Parent: Aggregate root, entity

Grammar: (INDEX | UNIQUE) '(' key (',' key)* ')' ((FOR | WHERE)? (EXPRESSION | name))?

Example:

module Finance
{
    guid root Invoice
    {
        int Year;
        string Number;
        date? Paid;
        specification NotPaid 'i => i.Paid == null';
        index (Number, Year) where NotPaid;
    }
}

Description:

Index can span multiple properties and can have a predicate implemented with specification.

Versioning:

Parent: Property in aggregate root or entity

Grammar: VERSIONING

Example:

module CMS
{
    root Post
    {
        string Text;
        timestamp ModifiedAt { versioning; }
    }
}

Description:

Server can auto maintain values for some properties on each insert and update.

Static values:

Parent: Aggregate root

Grammar: STATIC name URI

Example:

module Finance
{
    root Currency(Code)
    {
        string Code;
        string Name;
        static USD 'USD';
        static EUR 'EUR';
    }
}

Description:

Instead of using enums to define allowed values, static values can be used instead. Static value points to an object's URI.

History:

Parent: Aggregate root

Grammar: HISTORY

Example:

module CMS
{
    root Post
    {
        string Text;
        history;
    }
}

Description:

Change history can be tracked using history concept.