Advanced concepts grammar

The ultimate benefit of DSL is that with small declaration of the feature you can reap benefits of complex infrastructure code built to support it. As your infrastructure grows with more and more features using them becomes as easy as declaring them with a single keyword in DSL.

Features that can't be exposed within DSL are probably not well thought out. Custom code is always present and if you find yourself writing a lot of underlying code in DSL you've probably fallen into anti-pattern of using DSL as your programming environment instead of domain declaration.

Let's explore some of more advanced features exposed through DSL.

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

Calculated specification property:

Parent: Aggregate root, entity, value and snowflake

Grammar: CALCULATED name FROM (EXPRESSION | specification)

Example:

module Library
{
    aggregate Book
    {
        decimal Rating;
        specification IsAnyGood 'b => b.Rating > 30';
        calculated AnyGood from IsAnyGood;
    }
}

Description:

Predicate used as read only property.

Calculated expression property:

Parent: Aggregate root, entity, value and snowflake

Grammar: CALCULATED TYPE '[]'? '?'? name FROM EXPRESSION

Example:

module Billing
{
    aggregate Invoice
    {
        Item[] Items;
        calculated money Total from 'it => it.Items.Sum(i => i.Price)';
    }
    value Item
    {
        string Product;
        money Price;
    }
}

Description:

Expression used as read only property.

Optimistic concurrency:

Parent: Aggregate root persistence

Grammar: OPTIMISTIC CONCURRENCY

Example:

module Scraping
{
    root Scrape
    {
        timestamp At;
        map Headers;
        string RawPage;
        string[] Links;
        persistence { optimistic concurrency; }
    }
}

Description:

When persisting aggregates, old aggregate can be sent to check if there have been changes in the meantime. If optimistic concurrency is enabled, persistance will fail in case when there are changes between provided and actual data.

Report:

Parent: Module

Grammar: REPORT name

Example:

module Finance
{
    report PrintInvoice;
}

Description:

Report should be used as container for various data. Data for complex forms can be populated at once instead of making multiple roundtrips to the server. It is also used for sending this data to the reporting engine.

Report simple property:

Parent: Report

Grammar: (INT | LONG | STRING | DATE |...) '[]'? '?'? name

Example:

module Finance
{
    report PrintInvoice
    {
        guid ID;
    }
}

Description:

Data in report can be used for display purposes or as specification input for finding other data.

Report data property:

Parent: Report

Grammar: (module '.') reference '[]'? '?'? name (EXPRESSION (ORDER BY (n (ASC | DESC)? (, n (ASC | DESC)? )* ) (LIMIT (n | NUMBER))? (OFFSET (n | NUMBER))? )? Grammar: (LIST | SET | ARRAY ) < (module '.') reference > '?'? name (EXPRESSION (ORDER BY (n (ASC | DESC)? (, n (ASC | DESC)? )* ) (LIMIT (n | NUMBER))? (OFFSET (n | NUMBER))? )?

Example:

module Finance
{
    root Invoice
    {
        date DueDate;
        money Total;
    }
    report PrintInvoice
    {
        guid InvoiceID;
        date After;
        Invoice Found 'i => i.ID == InvoiceID';
        List<Invoice> Last5 'i => i.DueDate >= After' ORDER BY DueDate LIMIT 5;
    }
}

Description:

Data sources can be queried with provided LINQ expression. Property can be also used just as data container prepopulated from client.

Report count property:

Parent: Report

Grammar: count < (module '.') reference > name EXPRESSION?

Example:

module Finance
{
    root Invoice
    {
        date DueDate;
        money Total;
    }
    report PrintInvoice
    {
        guid InvoiceID;
        date After;
        Invoice Found 'i => i.ID == InvoiceID';
        List<Invoice> Last5 'i => i.DueDate >= After' ORDER BY DueDate LIMIT 5;
        count<Invoice> TotalAfter 'i => i.DueDate >= After';
    }
}

Description:

Count operation can be used to implement simple patterns such as paging with total number of results in a single call.

Report Templater:

Parent: Report

Grammar: TEMPLATER name document (TO? PDF)?

Example:

module Finance
{
    root Invoice
    {
        date DueDate;
        money Total;
    }
    report PrintInvoice
    {
        guid InvoiceID;
        Invoice Found 'i => i.ID == InvoiceID';
        templater CreatePdf 'Invoice.docx' pdf;
    }
}

Description:

Templater reporting library can be used to create various documents from predefined templates. More info about can be found at Templater site

Olap cube:

Parent: Module

Grammar: OLAP? CUBE < source > name

Example:

module Finance
{
    root Invoice
    {
        int Year;
        date DueDate;
        money Total;
    }
    snowflake<Invoice> InvoiceData { Year; DueDate; Total; }
    olap cube <InvoiceData> InvoiceCube;
}

Description:

Cube is used for quick analysis of data. It can be created from snowflakes, aggregates, entities and sql sources.

Olap cube dimensions:

Parent: Olap cube

Grammar: DIMENSION name (AS? alias)?

Example:

module Finance
{
    root Invoice
    {
        int Year;
        string Seller;
        date DueDate;
        money Total;
    }
    olap cube <Invoice> InvoiceCube
    {
        dimension Seller;
        dimension Year as Period;
        specification FilterData 'it => it.Seller == Who'
        {
            string Who;
        }
    }
}

Description:

Dimension is information by which analysis result will be grouped.

Olap cube facts:

Parent: Olap cube

Grammar: (SUM | COUNT | AVERAGE | DISTINCT | ...) name (AS? alias)

Example:

module Finance
{
    root Invoice
    {
        int Year;
        string Seller;
        date DueDate;
        date? Paid;
        money Total;
    }
    snowflake<Invoice> InvoiceData { Seller; Year; DueDate; Paid; Total; }
    cube<InvoiceData> InvoiceCube
    {
        dimension Seller;
        dimension DueDate;
        dimension Year;
        average Total AverageInvoice;
        sum Total TotalInvoice;
        count DueDate InvoiceCount;
        count Paid PaidCount;
    }
}

Description:

Fact is the way analysis will be done on some information.

Olap cube Templater:

Parent: Olap cube

Grammar: TEMPLATER name document (TO? PDF)?

Example:

module Finance
{
    root Invoice
    {
        int Year;
        string Seller;
        date DueDate;
        date? Paid;
        money Total;
    }
    cube<Invoice> InvoiceCube
    {
        dimension Seller;
        dimension DueDate;
        sum Total TotalInvoice;
        count DueDate InvoiceCount;
        count Paid PaidCount;
        templater CreateXlsx 'InvoiceData.xlsx';
    }
}

Description:

Templater reporting library can be used to populate spreadsheet or to create finished reported from some data analysis.