Building Microservices Through Event-Driven Architecture, Part 1: Application-Specific Business Rules

DZone 's Guide to

Building Microservices Through Event-Driven Architecture, Part 1: Application-Specific Business Rules

In this post, we take a cross-disciplinary look into creating microservices by applying the principles of event-driven design.

· Microservices Zone ·
Free Resource

Image source: https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html

Today, architectures such as the onion or hexagonal patterns play an important role in the testability and maintenance of code, its independence from external frameworks, etc. In this tutorial, I will show you how to use clean architecture, with methods and tools such as Domain-Driven Design (DDD), Test (Behavior) Driven Development (TDD), CQRS, Event Sourcing, Containerization, OAuth2, and Oidc to build a microservices architecture.

For more information on clean architecture, I suggest you read this article by Robert C. Martin (Uncle Bob).

The Dependency Rule

According to Uncle Bob,

"The concentric circles [in the graphic above] represent different areas of software. In general, the further in you go, the higher level the software becomes. The outer circles are mechanisms. The inner circles are policies.

"The overriding rule that makes this architecture work is The Dependency Rule. This rule says that source code dependencies can only point inwards. Nothing in an inner circle can know anything at all about something in an outer circle. In particular, the name of something declared in an outer circle must not be mentioned by the code in an inner circle. That includes functions, classes. variables, or any other named software entity.

"By the same token, data formats used in an outer circle should not be used by an inner circle, especially if those formats are generated by a framework in an outer circle. We don't want anything in an outer circle to impact the inner circles."

    - Robert C. Martin (Uncle Bob)

CQRS Separate Commands From Queries

Commands are operations that change the application state and return no data. Queries are operations that return data but do not change application state.

So, CQRS will be a very useful concept in the world of microservices by creating our application with two databases:

Microservices architecture

  1. A relational database which is optimized for writing on the command side.
  2. A NoSQL database on the query side in order to read data as fast as possible. Since most applications read data much more often than they write data, in our approach of containerization, we can scale the command side on two pods and query side on 10 pods, for example

CQRS fits into a domain-driven design model. DDD focuses on building rich domain models to tackle complex business logic.

Changing the data causes more bugs. So, having a clear perception of which part of the application changes the data and which part of the application does not change the data will help with maintainability and debugging

Event sourcing stores all changes to an object as a series of events in an event store

I will use this concept to build the following:

  • Domain service implements domain-related concepts (entity, value object, aggregates, domain events), records a command in a relational database, and an event in the event store — all as a Unit for the purpose of data change.
  • A producer takes the event from the event store and sends it to the service bus (an event store is an 'append only' table and uses ubiquitous language).
  • A consumer, the subscriber to the service bus, takes the event from the service bus and registers it as pre-computed data to a NoSQL database.
  • The ReadModel service queries the NoSQL database.
  • Everything that happens is saved in the event store.

I will build a system that helps speakers and attendees to register and follow events (conferences, talks, meetups, etc...).

My project is structured as follows:

  • The innermost layer holds the core domain. It contains our domain objects and business rules and defines our External Interfaces.

  • Databases, network connections, filesystem, UI, or special frameworks are not allowed.

  • The core domain has no knowledge of anything outside of itself.

  • Those dependencies, and their implementations, are injected into our core domain using Interfaces.

Test Driven Development

This layer points to the core domain and contains the application-specific business rules.

The above code allows us to:

  • Orchestrate the data flow and use the domain model.

  • Have no dependency to a database, UI, or special frameworks.

This layer holds the Web, UI, and Presenter concerns. In the context of our API, this means it accepts input in the form of HTTP requests over the network (POST/PUT/PATCH/DELETE) and returns its output as JSON formatted content.

This layer holds the Database and Gateway concerns. Here, we define the data access layer, repositories, etc.

It also contains the physical implementation of the interfaces defined in our domain layer

In the code below, we are implementing the "Speech Registration" use case:

To make my tests green, the first thing I need to do is implement RegisterSpeechUseCase:

The overriding rule that makes this architecture work is the Dependency Rule. This rule says that, "source code dependencies can only point inwards. Nothing in an inner circle can know anything at all about something in an outer circle." (Robert C. Martin (Uncle Bob)).

It takes an input object as a command and then renders the interface:

Then RegisterSpeechUseCase will look like this:

Let us defines dependencies such as IUnitOfWork and ISpeechRepository. These interfaces belong to the core domain and will be implemented in the infrastrucrure.

ISpeechRepository needs a Speech Entity, so let's create it on the core domain.

In this stage, every thing compiles succesfully, but my tests failed:

As you can see, tests fails because I verify that CreateAsync and commit are called. So, let us call:

Here we're calling SpeechRepository.CreateAsync and IUnitOfWork.Commit on the  RegisterSpeechUseCase class:

Then we create a mock of SpeechRepository.CreateAsync  and IUnitOfWork.Commit on the 'arrange' section of the unit test.

At this stage, all tests are green but my code coverage is not good enough:

For example, if I comment out this block my tests will succeed but my app will crash at runtime if the command is null.

Image title

Image title

Let us add a new test to fix it:

Image title

Finally, the code coverage is 100% for LogCorner.EduSync.Speech.Application

Image title

But what happens if I permute values?

var title = command.Type;
var urlValue = command.Title;
var description = command.Url;
var type = command.Description;

All tests will succeed but my application will be in an invalid state because it will insert the title instead of the URL.

I can fix this using moqSpeechRepository.Verify in test assertions, but I'll leave it like that and fix it when implementing my domain with the introduction of the values objects:

Image title

In the next post, I will implement the domain model.

Source code is available here: RegisterSpeechUseCase

clean code, event-driven architechture, microservices, microservices architecture

Published at DZone with permission of Gora LEYE , DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}