Counting events: denormalizing relationships in forecasts

I am working on CQRS / ES architecture. We run several asynchronous projections in the reading repositories in parallel, because some forecasts can be much slower than others, and we want to synchronize more with the recording side for faster projections.

I am trying to understand approaches to how I can generate reading models and how this can lead to data duplication.

Let me take an order with items as a simplified example. An order can have several items, each item has a name. Elements and orders are separate units.

I could either try to save the read models in a more normalized way, where I create an entity or document for each element and order, and then refer to them - or I might want to save it in a more denormalized way, where I have an order containing elements .

normalized

{
  Id: Order1,
  Items: [Item1, Item2]
}

{
  Id: Item1,
  Name: "Foosaver 9000"
}

{
  Id: Item2,
  Name: "Foosaver 7500"
}

Using a more normalized format would allow a single projection to process events that affect an element and orders for action, as well as update the corresponding objects. It also means that any changes to the item name affect all orders. For example, a client may receive an invoice for different goods than the corresponding account (for example, it is obvious that the model may not be good enough and lead us to the same problems as denormalization ...)

Denormalized

{
  Id: Order1,
  Items: [
    {Id: Item1, Name: "Foosaver 9000"},
    {Id: Item2, Name: "Foosaver 7500"},
  ]
}

, , , , . , , , , . , , , - ItemForOrder, ItemForSomethingElse - , ( , ).

Item , , . , .

, ?


2016-06-17

, . , , , , .

, , . - , , , .

// related data 
public class Item 
{
  public Guid Id {get; set;}
  public string Name {get; set;}
  /* and whatever else is needed but not provided by events */
}

// denormalised info for document
public class ItemInfo 
{
  public Guid Id {get; set;}
  public string Name {get; set;}
}

// denormalised data as document
public class ItemStockLevel
{
  public ItemInfo Item {get; set;} // when this is a document
  public decimal Quantity {get; set;}
}

// or for RDBMS
public class ItemStockLevel
{
  public Guid ItemId {get; set;}
  public string ItemName {get; set;}
  public decimal Quantity {get; set;}
}

, . -.

, . , -, .

, , ( , ?). , , , , ...

CQRS: ? ? , - .

+4
2

-, , . () . Udi Dahan " " , , , , , "" , .

,

// Assuming Orders come from Customers
OrderCreated(orderId: Order1, customerId: Customer1)

ItemAdded(itemId: Item1, orderId: Order1, Name:"Foosaver 9000")

ItemAdded(itemId: Item2, orderId: Order1, Name:"Foosaver 7500")

, - , , , ..

, , . , , : . , , , ; .

; , , , , .

( : ItemRemoved )

, , , . : - DTO, . , ( ), .

{
    Title: "Your order #{Order1}",
    Items: [
        {Name: "Foosaver 9000"},
        {Name: "Foosaver 7500"}
    ]
}

, , , , , , , .

, DTO ,

{
    Title: "Your order #{Order1}",
    refreshUrl: /orders/Order1?atLeastVersion=20,
    Items: [
        {Name: "Foosaver 9000", detailsUrl: /items/Item1?atLeastVersion=7},
        {Name: "Foosaver 7500", detailsUrl: /items/Item2?atLeastVersion=9}
    ]
}
+1

. , , , . , .

0

All Articles