Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Where to validate business rules when using event-sourcing

I implemented event sourced entities ( in Domain driven design it's called aggregate). It's a good practice to create a rich domain model. Domain driven design (DDD) suggests putting all business related things when possible into core entities and value objects.

But there is an issue when using such an approach in combination with event sourcing. In comparison to traditional approaches in an event sourced systems events are stored first and later all events are applied when building the entity to execute some methods.

Based upon that, the big question is where to put the business logic. Usually, I would like to have a method like:

public void addNewAppointment(...)

In this case, I would expect that the method makes sure that no business rules are violated. If this is the case an exception would be thrown.

But when using event sourcing I would have to create an event:

Event event = new AppointmentAddedEvent(...);
event store.save(event);

Right now, I explored 2 approaches to check business rules before storing the event.

First, check business rules within the application layer. The application layer in DDD is a delegation layer. Actually, it should contain no business logic. It should only delegate things like getting core entities, calling methods and saving things back. In this example this rule would be violated:

List<Event> events = store.getEventsForConference(id);

// all events are applied to create the conference entity
Conference conf = factory.build(events); 

if(conf.getState() == CANCELED) {
    throw new ConferenceClosed()
}

Event event = new AppointmentAddedEvent(...);
event store.save(event);

Obviously, the business rule adding appointments to canceled conferences should not be possible leaked into the non-core component.

The second approach I know is to add process methods of commands to core entities:

class Conference {
// ...

    public List<Event> process(AddAppointmentCommand command) {
        if(this.state == CANCELED) {
            throw new ConferenceClosed()
        }

        return Array.asList(new AppointmentAddedEvent(...));
    }

// ...
}

In this case, the benefit is that the business rules are part of the core entity. But there is a violation of separation of concerns principle. Now, the entity is responsible for creating events that are stored in an event store. Besides that, it feels weird that an entity is responsible for creating events. I can argue for why it's natural that an entity can process events. But the creation of domain events for storing, not for natural publishing, feels wrong.

Did anyone of you experience similar issues? And how did you solve these?

For now, I will just go with the business rules within the application service solution. It is still one place and ok-ish but it violates some of the DDD principles.

I am looking forward to your ideas and experiences about DDD, event sourcing and the validation of incoming changes.

Thanks in advance

like image 864
Journerist Avatar asked Jul 18 '18 18:07

Journerist


People also ask

What is the Event Sourcing pattern?

The event sourcing pattern is typically used with the CQRS pattern to decouple read from write workloads, and optimize for performance, scalability, and security. Data is stored as a series of events, instead of direct updates to data stores.

When should I use Event Sourcing?

Indeed, Event Sourcing shines the most when we can work with the business to find the business events. Event Storming and Event Modeling proved that events work great as a way to describe business processes. We can use them as “checkpoints” of our workflow. Events are also essential as a data model.

What is Event Sourcing database?

Event sourcing stores the state of a database object as a sequence of events – essentially a new event for each time the object changed state, from the beginning of the object's existence. An event can be anything user-generated – a mouse click, a key press on a keyboard, and so on.

What is Event Sourcing example?

Good examples for Event Sourcing are version control systems that stores current state as diffs. The current state is your latest source code, and events are your commits.


1 Answers

I love this question. When I first asked it, that was the break between just following the patterns and challenging myself to understand what is really going on.

the big question is where to put the business logic

The usual answer is "the same place you did before" -- in methods of the domain entities. Your "second approach" is the usual idea.

But there is a violation of separation of concerns principle.

It isn't really, but it certainly looks weird.

Consider what we normally do, when saving off current state. We run some query (usually via the repository) to get the original state out of the book of record. We use that state to create an entity. We then run the command, in which the entity creates new state. We then save the object in the repository, which replaces the original state with the new state in the book of record.

In code, it looks something like

state = store.get(id)
conf = ConferenceFactory.build(state)
conf.state.appointments.add(...)
store.save(id, conf.state)

What we are really doing in event sourcing is replacing a mutable state with a persistent collection of events

history = store.get(id)
conf = ConferenceFactory.build(history)
conf.history.add(AppointmentScheduled(...))
store.save(id, conf.history)

In mature business domains, like accounting or banking, the ubiquitous language include event histories: journal, ledger, transaction history,... that sort of thing. In those cases, event histories are an inherent part of the domain.

In other domains -- like calendar scheduling -- we don't (yet?) have analogous entities in the domain language, so it feels like we are doing something weird when we change to events. But the core pattern is the same -- we pull history out of the book of record, we manipulate that history, we save the updates to the book of record.

So the business logic happens in the same place that it always did.

Which is to say that yes, the domain logic knows about events.

An exercise that may help: let go of the "object oriented" constraint, and just think in terms of functions....

static final List<Event> scheduleAppointment(List<Event> history, AddAppointmentCommand addAppointment) {

    var state = state(history)

    if(state == CANCELED) {
        throw new ConferenceClosed()
    }

    return Array.asList(new AppointmentAddedEvent(...));
}

private static final State state(List<Event> history) {...}
like image 115
VoiceOfUnreason Avatar answered Nov 03 '22 06:11

VoiceOfUnreason