Report

Data retrieval pattern on top of LINQ

Round-trips to the server are one of the most common source of performance issues. While it's common to reduce server round-trips to a single call, server usually has multiple round-trips to the database too.

Report concept can be used to declare data source and its filters using specification expression. Limit, offset and order can be used too. Since result of previous expression can be used as filter for the next one, this pattern can be used to query all kinds of data.

aggregate Order
{
  date placed;
  date? deadline;
  string customer;
  List<LineItem> items;
}

entity LineItem
{
  string productName;
  decimal quantity;
}

report OrderReport
{
    int? total;
    Set<string> customers;
    List<Order> ordersByCustomers 
        'it => customers.Contains(it.customer)'
        limit total
        order by placed asc;
    List<Order> allOtherOrdersInThatRange
        'it => !customers.Contains(it.customer)
            && ordersByCustomers.FirstOrDefault() != null
            && ordersByCustomers.FirstOrDefault().placed <= it.placed
            && ordersByCustomers.LastOrDefault().placed >= it.placed'
        order by placed asc;                
}

In this example OrderReport will be returned from the server, based on provided total and customers parameters. Order by customers will be filtered based on customers property. All other orders except that customer will be filtered using provided parameter and result of the previous query.

Report data source and query types

All kind of data sources can be queried in report concept: Aggregates, Snowflakes, Linq and SQL. If only single result is required appropriate data type can be used. So instead of

snowflake OrderGrid from Order { placed; customer; }

report LastOrderReport
{
    string searchFor;
    List<OrderGrid> ordersByCustomer 'it => searchFor == it.customer' limit 1;
}

we can use more natural one:

report LastOrderReport
{
    string searchFor;
    OrderGrid orderbyCustomer 'it => searchFor == it.customer';
}

Report is also a first class citizen in domain model. Non-trivial screens usually require several data sources which can be queried in a single call using report concept. Combining snowflakes to avoid lazy loading and report to aggregate several calls to server in a single request will result in optimal performance.

Creating document reports server side

Report concept is often used to create server side documents/reports. Data populated in report can be sent to reporting library which will create document based on provided data. Templater is a simple library which can be used to populate reports server side.

report CustomerOrders
{
    string customer;
    date fromDate;
    date? untilDate;
    List<Order> orders 
        'it => customer == it.customer
            && fromDate <= it.placed
            && (untilDate == null || untilDate >= it.placed)';
    templater CreateReport 'CustomerOrders.docx' pdf;
}

When client requests CreateReport in CustomerOrders concept server will return pdf file populated from CustomerOrders.docx template. More info on how Templater works can be found on its website.