Contact Me

Event Driven Architecture ( EDA )

Architecture that uses events to trigger and communicate between decoupled services

 (EDA) is a pattern where the flow of the application is determined by events or messages that are emitted by various components of the system. These events can be triggered by user actions, system events, or other external factors, and they are typically handled by event listeners or subscribers that respond to the event in some way.

 Generally, a message broker is used as an intermediary for the reception and delivery of these events. As shown in the picture


EDAImg

 Along with this pattern, CQRS (Command Query Responsibility Segregation) is also used.


CQRS (Command Query Responsibility Segregation)

 It states that every method should either be a command that performs an action, or a query that returns data to the caller, but not both.

Asking a question should not change the answer.


 In a CQRS system, the read model and the write model are decoupled, meaning that they can evolve independently and be optimized for their specific purpose. The read model is responsible for querying the data and returning it to the user, while the write model is responsible for updating the data and ensuring its consistency. Because of these characteristics, it is often used with event sourcing.


Event sourcing

 Is a technique that is often used in conjunction with EDA and CQRS. In event sourcing, all changes to the application state are captured as a sequence of events, and the current state of the system is derived by replaying these events. This approach has several benefits, such as providing a complete history of all changes to the system, making it easy to roll back to a previous state, and enabling the creation of multiple read models from the same event stream.


Here is an example of how EDA, CQRS, and event sourcing can be implemented in JavaScript.

// Define an event for creating a new user

class UserCreatedEvent {

constructor(userId, name) {

  this.userId = userId;

  this.name = name;

}

}

// Define a command for creating a new user

class CreateUserCommand {

constructor(userId, name) {

  this.userId = userId;

  this.name = name;

}

}

// Define a write model for handling the create user command

class UserWriteModel {

constructor() {

  this.users = {

};

}

handleCommand(command) {

  if (command instanceof CreateUserCommand) {

   if (this.users[command.userId]) {

    throw new Error(`User with ID ${command.userId} already exists`);

   }

  this.users[command.userId] = {name: command.name };

   return new UserCreatedEvent(command.userId, command.name);

  }

}

}

// Define a read model for handling the user created event

class UserReadModel {

constructor() {

  this.users = {};

}

handleEvent(event) {

  if (event instanceof UserCreatedEvent) {

   this.users[event.userId] = { name: event.name };

  }

}

}

// Create an event store to hold the events

const eventStore = [];

// Create a write model and a read model

const writeModel = new UserWriteModel();

const readModel = new UserReadModel();

// Handle a create user command

const createUserCommand = new CreateUserCommand(1, 'John Doe');

const event = writeModel.handleCommand(createUserCommand);

eventStore.push(event);

readModel.handleEvent(event);

console.log(readModel.users); // {'1': {name: 'John Doe' } }