1

In eventsourcing, I am having bit confusion on where exactly have to apply Business logic? I have already searched in google, but all examples are very basic ie., Updating state of an object inside Handler from an event object, but in my other scenario, had some confusion didnt understood on where exactly have to apply Business logic.

For eg: lets take a scenario to update status of IntervieweeVO, which exists inside Interview aggregate class as below:

class Interview extends AggregateRoot {

  private IntervieweeVO IntervieweeVO;

}

class IntervieweeVO {
  int performance;
  String status;
}

class IntervieweeSelectedEvent extends BaseEvent {
  private IntervieweeVO IntervieweeVO;
}

I have a business logic, ie., if interviewee performance < 3, then status = REJECTED, otherwise status should be SELECTED.

So, my doubt is: where should I keep above business logic? Below are 3 scenarios:

1) Before Applying an Event: Do Business Logic, then apply(IntervieweeSelectedEvent) and then eventstore.save(intervieweeSelectedEvent)

2) Inside EventHandler: Apply Business logic inside EventHandler class, like handle(IntervieweeSelectedEvent intervieweeSelectedEvent) , check Business logic and then update Object state in ReadModel table.

3) Applying Business Logic in both places ie., Before Applying an event and also while handing the event (combining above 1 + 2)

Please clarify me on above.

john
  • 925
  • 1
  • 12
  • 20
  • any idea on above pls? – john Sep 24 '20 at 18:21
  • 1
    You should be able to reconstruct the entity's state as of a specific point in time from the event stream meaning that applying events should probably not be responsible for logic that will change over time, but rather merely responsible for projecting the entity's state. Business logic would go in entity methods generating the events IMO. If the `performance < 3, then...` rule is different today than it was back then, you still want to able to project the old state as it was with the old rule. – plalx Sep 24 '20 at 21:32
  • @plalx you mean Business logic should be inside apply(IntervieweeSelectedEvent) method? – john Sep 25 '20 at 03:15
  • 1
    No, `apply` is just to project the state, there shouldn't be any business logic there. `entity.doSomething() --> apply(SomeThingHappened)`. Logic would be in `entity.doSomething()`. – plalx Sep 25 '20 at 03:17
  • @plalx okay, you mean business logic should be applied before applying an event? and not while handling an event at all? pls correct me if I still misundestood, as I am very new to this eventsourcing. – john Sep 25 '20 at 03:31
  • 1
    Yes, because otherwise if business logic changes when you recreate the entity from the events you wouldn't have the same state if logic is in `apply`. – plalx Sep 25 '20 at 04:36
  • 1
    E.g. in your case you may have `interviewee.assessPerformance(POOR)` which yields `IntervieweePerformanceAssessed` and `IntervieweeRejected` events I think. Then if the rule changes you may call `interviewee.reevaluateSmartScreeningPolicy()` or something like that. – plalx Sep 25 '20 at 04:48
  • Have you had the time to review some of the answers? – plalx Oct 11 '20 at 13:27

3 Answers3

2

The main issue with event sourcing is that it is hard to produce a viable example using synthetic scenarios.

But probably I could suggest something a little bit better than Interview. If you compare pre-computer era event sourced systems, you'll find that an event stream, which is the store of events composing the lifecycle of some entity, it rather a long-living thing. Events in an entity could span a few days (a list that tracks some document flow), a year (accounting period for some organisation) or tens of years (medical records for some person).

A single event stream usually represents a single entity - a legal process, a ledger or a person... Each event is a transactional (as in ACID) change to the state of the entity.

In your case such an entity could be, say, a position. Which is opened, announced, interviewee invited, invitation accepted, skills assessed, offer made, offer accepted, position closed. From the top of my head.

When an event is added to an entity, it means that the entity's state has changed. It is the new truth about the entity. You want to be careful about changing the truth. So, that's where business logic happens. You run some business logic to make up the decision whether to change the truth or not. It you decide to update the state of the truth - you save the event. That being said, "Interviewee rejected" is a valid event in this case.

Since an event is persisted, all the saved events of an entity are unconditionally the part of the truth about the entity, in their respective order. You then don't decide whether to "accept" or "reject" a persisted event - only how it would affect a projection.

iTollu
  • 999
  • 13
  • 20
1

you tagged your question cqrs but this is acutally the missing part in your example.

Eventsourcing is merely a way to look at the current state of an object. You either save that state as it appears now, or you source it from everything that happend. (eg Bank accounts current banalance as value or sum of all transactions)

So an event is a "fact" of something that happend. In your case that would be the interview with a certain score. And (dependent on your business logic) it COULD also state the status if the barrier is expected to change over time.

The crucial point is here that you should always adhere to the following chain:

"A command gets validated and if it passes it creates an unchangeable event that is persisted"

This means that in your case I would go for option 1. A SelectIntervieweeCommand should be validated and if everything is okay create an IntervieweeSelectedEvent which is an unchangeable fact. Thus the business logic wether the interviewee passed or not, must reside in the command handler function.

winson
  • 388
  • 1
  • 9
1

You should be able to reconstruct the entity's state as of a specific point in time from the event stream.

This implies that applying events should NOT contain any logic other than state mapping logic. All state necessary to project the AR's state from the events must be explicitly defined in those events.

Events are an expressive way to define state changes, not operations/commands. For instance, if IntervieweeRejected means IntervieweeStatusChanged(rejected) then that meaning can't ever change. The IntervieweeRejected event can't ever imply anything else than status = rejected, unless there's some other state captured in the event's data (e.g. reason).

Obviously, the way the state is represented can always change, but the meaning must not. For example the AR may have started by only projecting the current status and later on projected the entire status history.

apply(IntervieweeRejected) => status = REJECTED //at first
apply(IntervieweeRejected) => statusHistory.add(REJECTED) //later

I have a business logic, ie., if interviewee performance < 3, then status = REJECTED, otherwise status should be SELECTED.

Business logic would be placed in standard public AR methods. In this specific case you may expect interviewee.assessPerformance(POOR) to yield IntervieweePerformanceAssessed(POOR) and IntervieweeRejected events. Should you need to reevaluate that smart screening policy at a later time (e.g. if it has changed) then you could implement a reevaluateSmartScreeningPolicy operation.

Also, please note that such logic may not even belong in the Interviewee AR itself. The smart screening policy may be seen as something that happend after/in response to the IntervieweePerformanceAssessed event. Furthermore, I can easily see how a smart screening policy could become very complex, AI-driven which could justify it living in a dedicated Screening bounded context.

Your question actually made me think about how to effectively capture the context or why events occurred and I've asked about that here :)

plalx
  • 42,889
  • 6
  • 74
  • 90
  • Many Thanks for your brief reply :) One doubt: Does business logic mean changing the state of domain model? If yes, then changing the state of domain should be inside the apply() method, right? – john Oct 16 '20 at 04:50
  • 1
    No, business logic would decide what's the new state given a starting state and a command. Events are then recorded to capture those decisions (the new state). The apply method is only responsible to materialize the new state in memory so decisions can be made for the next command. In short, apply would not need to project all the state within events if such state is not used to make business logic decisions. A good example may be not projecting a `description` because the value is never involved indecision making. – plalx Oct 16 '20 at 12:10