booster icon indicating copy to clipboard operation
booster copied to clipboard

Allow one event type to be reduced by multiple entities

Open NickSeagull opened this issue 3 years ago • 3 comments

Feature Request

Description

Nowadays, one event must be reduced by one entity due to the sharding algorithm. The solution to this is to create an event handler that creates a new event that will be reduced in the other entity.

It'd be cool to allow reducing the same event type by multiple entities. I know this is not possible due to ordering and stuff, BUT, with the proposed solution it can be done.

Possible Solution

Instead of reducing the same event directly by the two entities, introduce an addition to the event reduction process.

As an example, imagine we have two entities:

  • User - Has a teams field, which contains the IDs of all the teams that this user is part of
  • Team - Has a members field, which contains the IDs of all the users that this team has

And we have an event called UserJoinedTeam that will add a Team ID into the User.teams. But here comes the issue: we are forcing the users to:

  1. Create an additional, artificial event to allow the Team entity to add the user to the team (something along the lines of TeamMembersUpdateTriggered)
  2. Create an event handler that does nothing but copy one event into another, so the entity can reduce the event.

Instead of failing with The event UserCreated was already registered to be reduced by method reduceUserCreated in the entity User. when registering it under the Team entity we should create an artificial "event handler" under the hoods that will be converting UserCreated events that have entityTypeName: User in its envelope entityTypeName field to UserCreated events that have entityTypeName: Team in its envelope entityTypeName field.

This would result in duplication of events, but it's exactly the same as making the user do it by hand.

NickSeagull avatar Jul 14 '21 13:07 NickSeagull

This is a limitation of the current implementation that many people have hit before. I've been recently reviewing documentation and hit this article from Martin Fowler's blog.

The article differentiates three event-driven patterns (TL;DR. Notification events, state transfer events, and state change events (event sourcing), but the article is worth reading in full). In Booster, all events are state change events over a specific entity. Still, an event handler can also intercept any event, so they can also behave as a notification event (that triggers some other action, ideally a command, but we don't allow this at the moment) or a state transfer event (the handler intercepts the event and generates a state change event for the second entity, similarly to what you described in your first paragraph). Still, we lack pure notification events that are not naturally attached to any specific entity (for instance, an external event, let's say, a BTC price change or a moon phase change). There's no way to model this kind of event right now, leading to weird implementations for these scenarios, being forced to create artificial entities that don't really exist.

I think that we should avoid creating "join objects" or any other kind of artificial artifact because this slides away from Booster's goal of sticking to real business logic. The closer we can be to the problem semantics, the better, so, if in the real world we can find examples of events that can't be represented as a specific entity state change, we should probably design some way to represent notification events within Booster, as well as mechanisms to intercept and react to these events (regular event handlers could work if we guarantee that all notification events of the same type will be processed sequentially).

Well, back to earth, I think that what I'm trying to say here is that if a state change in one entity must change the state of a second entity, it could make sense to model it intercepting the first event with an event handler and generating a second state change event for the second entity 😜

javiertoledo avatar Jul 14 '21 16:07 javiertoledo

🤔 Yeah, this makes a lot of sense, but still there are many cases that we have to create not only artificial events, but also artificial entities to allow doing some kind of work to join data (e.g. https://github.com/boostercloud/booster/discussions/882#discussioncomment-932854).

Yes, our goal is to stick to business logic, but I think that Event Sourcing + CQRS modelling, as all kind of models, sometimes are not 100% accurate for the real world. Booster's key values are also that you don't have to be an expert in DDD/ES/CQRS in order to use, and creating these kind of constructs would help.

Also, this thing that just came to my mind. Having CRU~~D~~ entities make no sense in the perfect modelling of ES+CQRS, yet there could be a form management app that literally you only create reviews, update them, or delete them. So having some artificial construct like @CRUEntity could make sense.

What I want to say here isn't that we should add all of the features, but there could be many that ease the usage of the framework a lot 😁

NickSeagull avatar Jul 15 '21 10:07 NickSeagull

It'd be cool to allow reducing the same event type by multiple entities. I know this is not possible due to ordering and stuff, BUT, with the proposed solution it can be done.

@NickSeagull What are those "ordering and stuff" limitations? Aren't the events ordered sequentially by their insertion time in the event store or do they have an extra ordering index that can break things if more entities get involved? 🤔

MarcAstr0 avatar Jul 29 '21 16:07 MarcAstr0