Common projection use cases

Aggregating data to a list

A common use-case is collecting some data across all aggregates for a given type. In the example below we want to be able to get a list of all registered Customers from a single query to a Projection.

Example Events

Let's say two Customers have been registered in our system with the following two events:

{
  "aggregateId":"723ecfce-14e9-4889-98d5-a3d0ad54912f",
  "events":[
    {
      "eventId":"127b80b5-4a05-4774-b870-1c9a2e2a27a3",
      "eventType":"CustomerRegisteredEvent",
      "data":{
        "customer": "Acme Inc"
      }
    }
  ]
}

and

{
  "aggregateId":"cf26e6df-a883-4fbc-929b-47052f0acdb8",
  "events":[
    {
      "eventId":"982f8747-ba7d-470e-beca-1582c48d3181",
      "eventType":"CustomerRegisteredEvent",
      "data":{
        "customer": "The Trading Company"
      }
    }
  ]
}

Projection definition

We can create an aggregated Projection that appends all Customer names to a field in our Aggregated Projection called registered-customers.

{
  "aggregated": true,
  "projectionName": "registered-customers",
  "feedName": "customer",
  "handlers": [
    {
      "eventType": "CustomerRegisteredEvent",
      "functions": [
        {
          "function": "append",
          "eventSelector": "$.event.customer",
          "targetSelector": "$.projection.customers"
        }
      ]
    }
  ]
}

Results

When we query the Aggregated Projection we will now get the following result.

GET /projections/aggregated/registered-customers

{
  "projectionId" : "registered-customers",
  "createdAt" : 1523518145532,
  "updatedAt" : 1523518146253,
  "data": {
    "customers": ["Acme Inc", "The Trading Company"]
  }
}

Adding and removing from lists

Let's say we're developing support meetings, where participants can be invited and later accept or reject the invitation. In this scenario a reasonable Projection would be to view all participants that currently have accepted their invitation.

Example Events

An example of a events for two participants that accepted their invitations could look like this:

{
  "aggregateId":"723ecfce-14e9-4889-98d5-a3d0ad54912f",
  "events":[
    {
      "eventId":"127b80b5-4a05-4774-b870-1c9a2e2a27a3",
      "eventType":"InvitationAcceptedEvent",
      "data":{
        "userId": "673b3926-3667-4daf-9db7-03c08adc5ffd"
      }
    }
  ]
}
{
  "aggregateId":"723ecfce-14e9-4889-98d5-a3d0ad54912f",
  "events":[
    {
      "eventId":"1a20dfac-ae44-49ac-9491-bcb6d418c4d0",
      "eventType":"InvitationAcceptedEvent",
      "data":{
        "userId": "fa0d1d7c-9c64-4d65-b000-e1ace3a67bb2"
      }
    }
  ]
}

Later, one of the participants rejects the invitation:

{
  "aggregateId":"723ecfce-14e9-4889-98d5-a3d0ad54912f",
  "events":[
    {
      "eventId":"127b80b5-4a05-4774-b870-1c9a2e2a27a3",
      "eventType":"InvitationRejectedEvent",
      "data":{
        "userId": "673b3926-3667-4daf-9db7-03c08adc5ffd"
      }
    }
  ]
}

Projection definition

{
  "aggregated": false,
  "projectionName": "meetings",
  "feedName": "meeting",
  "handlers": [
    {
      "eventType": "InvitationAcceptedEvent",
      "functions": [
        {
          "function": "append",
          "targetSelector": "$.projection.participants"
        }
      ]
    },
    {
      "eventType": "InvitationRejectedEvent",
      "functions": [
        {
          "function": "remove",
          "targetSelector": "$.projection.participants[?]",
          "targetFilter": "[?(@.userId == $.event.userId)]"
        }
      ]
    }
  ]
}

Results

When we query the Aggregated Projection we will now get the following result.

GET /projections/sing/meetings/723ecfce-14e9-4889-98d5-a3d0ad54912f

{
  "projectionId" : "723ecfce-14e9-4889-98d5-a3d0ad54912f",
  "createdAt" : 1523518145532,
  "updatedAt" : 1523518146253,
  "data": {
    "participants": [{
      "userId": "fa0d1d7c-9c64-4d65-b000-e1ace3a67bb2"
    }]
  }
}