Preferring microservices to monolith architecture means more challenges. Most difficult ones are: distributed transactions, distributed locking, communication via message broker, and of course enabling events. Most of these problems have well-known solutions named patterns. CQRS, Saga, Event sourcing. They all referring to event-driven architecture, and unfortunately, they also increase the complexity of the entire system. So increasing complexity we are making our software less maintainable. Can we design our microservices better?

To answer on this question firstly we need to dive deeply into event-driven architecture and its components.

Events

Each event is immutable, which is good for maintainability. The same as object, good event should be small and immutable. To read the data we are composing events into projections and show then. Simple example: UserAdded:

{
  "eventType": "UserAdded",
  "id" : "256",
  "payload" : {
    "username" : "yegor256"
  }
}

UserChangeName:

{
  "eventType": "UserChangeName",
  "id" : "256",
  "payload" : {
    "username" : "YEGOR256"
  }
}

User projection:

final class User {
  private final Long id;
  private final String username;
}

Events enabling read/write flexibility, ability to form event/data streaming, and the most important: your application’s state is consists of the events, that you store.

Event store

Event store is the place where we store all the events. Usually that’s a databases.

Event bus

Event bus is just a pipeline of events. Like Kafka, RabbitMQ, or SQS.

Event sourcing

“The fundamental idea of Event Sourcing is that of ensuring every change to the state of an application is captured in an event object, and that these event objects are themselves stored in the sequence they were applied for the same lifetime as the application state itself.” - Martin Fowler Simply put, event sourcing - is the process of persisting business entities as a sequence of events.

CQRS

CQRS is an architectural pattern acronym, standing for Command Query Responsibility Segregation. It divides a system’s actions into commands and queries. It is related to CQS, which is Command Query Separation. CQS is an architectural pattern, and the acronym stands for Command Query Separation. CQS is the core concept that defines two types of operations handled in a system: a command that executes a task, a query that returns information, and there should never be one function to do both of these jobs. The term was created by Bertrand Meyer in his book Object-oriented Software Construction (1988, Prentice Hall).

Also, we need some sort of distributed transactions:

Saga

In microservices, each service has its own database. Thus, business transactions should be also split into multiple services. So, local transaction won’t work. In this case we need to design distributed transaction management and also, compensating transactions too. This pattern really brings complexity. My advice is to try to design less coupled services. Always think about transactional logic when you are making services decomposition.

Now to problems: complexity and maintainability.

Main complexity we got from frameworks that we are creating and using. Our microservices are so complex and unmaintainable not because of Kafka or CQRS involvement. But because we use such frameworks, that are designed without any simplicity and maintainability in mind. Just look at the Spring Cloud or more event-oriented Eventuate.

So, we need to form principles and then construct dedicated frameworks.

That’s why I start designing EO-CQRS, OOP event sourcing & cqrs framework, that tries to reduce the complexity of microservices architecture. And you can help by submitting issues and pull requests!