charlessolar / Aggregates.NET

.NET event sourced domain driven design model via NServiceBus and GetEventStore

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Keeping track of children

charlessolar opened this issue · comments

We currently support children getting information from their parent via

this.Parent

we can support a parent getting it's children like so

this.Children<ChildEntity>()

which would read all ChildEntity streams and return a list of ChildEntities.

Child entity stream ids would be in metadata? Or perhaps an internal Aggregates.Net event

  • Option A (Child streams in parent metadata)
    Not ideal since when a child a created we'll be doing a double commit - potentially saving parent metadata without actually saving the child (or vice versa). Would also require parent's being aware a child is being created to figure out they need to update their metadata..

  • Option B (Internal Aggregate.NET event)
    Each entity would have an initial "Instantiated" event Agg.net would create when .New is used. This event would contain the parent information and other entity-related things that could be useful.
    Also not great as we'd be messing with the user's streams (in a small way) and potentially making migrations and upgrades harder.

  • Option C (Aggregates.NET event not written to entity stream)
    This would involve raising some event when a new entity is created as well, but not saving the event to the entity stream. Perhaps we'd have a "Aggregates.NET.Data" entity which would be raise events whenever a new entity is defined.
    Not ideal because of the double commit problem.

  • Option D (Internal Aggregates.NET projection) probably the best option
    We'd write and make sure a projection exists which reads all events, looks over the metadata, and partitions the state based on stream id. EventStore would create a state object for each entity stream, and add children to a simple list when it processes a new event with the Parent metadata

Aggregate.NET metadata for parents currently only contains the parent's stream id

{
  "EventId": "fdc51d57-deac-4f47-aa63-a9210064653a",
  "EntityType": "eShop.Identity.User.Entities.Role.Role, eShop.Identity.Domain.Entities, Version=0.1.0.0, Culture=neutral, PublicKeyToken=null",
  "StreamType": "DOMAIN",
  "Bucket": "default",
  "StreamId": "2a7f6c3a-f218-4968-bf8c-b2fb9e7fa581",
  "Parents": [
    "Lucas29"
  ],
  "Compressed": false,
  "Version": 0,
  "Timestamp": "2018-07-18T06:05:31.720422Z",
...

In order for option D to work we'll need to store the parent's entity type as well.

Perhaps turning the parents array into something like

Parent: [
{ EntityType: 'eShop.Identity.User.User, eShop.Identity.Domain.Entities, Version=0.1.0.0, Culture=neutral, PublicKeyToken=null', Id: 'Lucas29' }
]

I am going to tie this issue into #20 - as it requires somewhat of a redesign revolving around event names and types

Using the new Parent array in metadata - an eventstore projection of:

options({
    $includeLinks: false,
    reorderEvents: false,
    processingLag: 0
});

fromCategory('DOMAIN')
.partitionBy(function(event) {
    let metadata = JSON.parse(event.metadataRaw);
    if(metadata.Parents === null || metadata.Parents.length === 0)
        return undefined;
    let lastParent = metadata.Parents.pop();
        
    let streamId = 'CHILDREN' + '-' + metadata.Bucket + '-[' + metadata.Parents.join(':') + ']-' + lastParent.EntityType + '-' + lastParent.Id;
        
    return streamId;
})
.when({
    $init: function() {
        return {
            children: []
        };
    },
    $any: function(state, event) {
        let metadata = JSON.parse(event.metadataRaw);
        if(metadata.Version !== 0)
            return state;
            
        state.children.push({ EntityType: metadata.EntityType, StreamId: metadata.StreamId });
        return state;
    }
})
.outputState();

Can successfully create streams tracking child entities. Resulting stream states look like:

Stream: $projections-working-CHILDREN-default-[]-Language.World v1-World-result

Data
{
  "children": [
    {
      "EntityType": "Language.Message v1",
      "StreamId": "f51aa6de-35a8-47ec-a45e-30297bd3ef65"
    },
    {
      "EntityType": "Language.Message v1",
      "StreamId": "9cf6fbed-e832-4338-9960-7e8bdbf0129b"
    }
]
}