Snowflake schema

Extending Domain-Driven Design

DDD described by Evans is an excellent introduction into domain modeling territory. Let's enrich it with another concept: projection on an aggregate.

In OLAP terminology Snowflake schema is used as normalized data structure. It's not as popular as star schema, but it maps closely to the projection on an entity.

Snowflake has a few unique properties:

  • it starts from a single entity
  • can navigate through relationships to other entities
  • will maintain 1:1 relationship with starting entity
  • can pick and choose interesting fields
  • it is allowed to start even from an entity which is not an aggregate

When we want to project an aggregate, but pick only some of its fields and include some others not available in an aggregate, but through a navigation, snowflake is a perfect fit for such use case.

aggregate Customer
{
  string name;
}

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

entity LineItem
{
  string productName;
  decimal quantity;
}

snowflake OrderInfo from Order
{
  placed;
  deadline;
  customer.name customerName;

  order by deadline desc;
}

In this example it's explicit in the model that we have an OrderInfo object which doesn't include line items, but projects customer name (if there is one) into it.

Consuming snowflakes and extending DDD

Snowflake is a first class citizen of domain model and allows for the most complicated parts of the system to consume data. Since it's just a read only data source, specifications can be used to query it. It can be used as data source for olap cubes. Calculated properties can be created in it. Because it's based off an aggregate, you can query it even using specifications defined on that aggregate.

Since snowflake URI is same as in its entity, this can be used in various situations for some optimizations. Instead of sending an object, only string representing the URI can be sent.

In Domain-Driven Design entities should have identity local to its aggregate and that you should not be allowed to access entity directly, but only by navigating from the aggregate root. But, since an entity has unique local identity inside an aggregate and aggregate has unique global identity, this means that entity has unique global identity too. While it seems good practice not to reference another entity, this only makes sense in a way that you are not allowed to modify it.

While DSL Platform doesn't allow you to query an entity (purely from ideological reasons) it allows you to create a snowflake from this entity. This can be a workaround when you really, really want to query an entity, but are not allowed.

Since entities are really useful as data source for cubes, there must be a way to query them (without navigating through the aggregate) and snowflakes are one solution to it. DSL Platform allows access to raw sql, so you can define your data source as detailed as possible if snowflakes are not adequate.