History Service

Introduction

During the application's operation, various account-related events will be created and published to the application message bus. We want to capture these events and persist them for auditing and analysis purposes. In this article, we build the Account History Service, which provides a historical log for each account-related operation performed by the application. The service binds to the message broker and receives all account-related events published, and persists these events to a MongoDB database. Additionally, a REST interface provides an endpoint to provide query facilities.

Requirements

Before you get started, you will need the following:
  • SDKMan
  • Java
  • Maven
  • Docker
  • Docker-Compose
Refer to the Development Toolbox article if you do not have these installed locally.

Building the Account History Service

Account History Hexagonal diagram

The AccountHistoryService that we are implementing is a simple service. The bulk of the service is composed of a QueueListener and the various event repositories. We will be persisting events to MongoDB using the same rationale we had for the AccountProfileService, flexibility. This approach gives the reference implementation the flexibility to evolve the account history data model to meet an as-yet-unknown target application's needs.

Account Events

The AccountHistoryService currently listens for the following events:

Account Events
Event Type Event Source
AccountRegisteredEvent AuthenticationService
CredentialsAuthenticationRequestedEvent AuthenticationService
PasswordChangedEvent AuthenticationService
PasswordRecoveryRequestedEvent AuthenticationService
PasswordRecoveryCompletedEvent AuthenticationService
AccountStatusChangedEvent AuthenticationService
ProfileCreatedEvent AccountProfileService
ProfileUpdatedEvent AccountProfileService
ProfileDeletedEvent AccountProfileService
ProfileImageCreatedEvent AccountProfileService
ProfileImageUpdatedEvent AccountProfileService
ProfileImageDeletedEvent AccountProfileService
As you evolve your application, this list will grow as new account-related use-case events are added.

The source

Maven pom.xml

loading...

bootstrap.yml

loading...

Not much different in this bootstrap file except for the spring.application.name

application.yml

loading...

Queue Event Listener

All events persisted in the Account History Service originate as events delivered to the service via the QueueEventListener. This listener binds to the message queue's AccountEvents input destination defined in the application.yml configuration file. This binding is configured with the input group accountHistoryGroup to allow multiple instances of the Account History Service to be deployed while ensuring that the event message will only be delivered to a single instance for processing.

loading...

As event messages arrive, they are routed to the appropriate handler method based on the contents of the message's headers['type'] value. We add the @StreamListener annotation to each message handler method and include the condition value expression to match the headers['type'] value. Each handler method then persists the event to the corresponding repository and updates the relevant custom service metrics. As the application evolves and new use cases require additional event types, we can add new @StreamListener methods to the QueueEventListener and configure the condition expression accordingly.

Polymorphic Event Repository

Our goal is to persist each of these events in a single MongoDB Collection. To accomplish this, we will be building a PolymorphicEventRepository. This approach allows us to map multiple event repositories to the same collection, and then query the collection to return events objects of differing types. By aggregating different event types within the same collection, we can simplify our query mechanism and avoid the need to make (n) queries across (m) unique event collections. To accomplish this, we create a repository interface for each corresponding event type. We use this interface to add events, and to perform queries exclusive to this specific event type. However, we query the repository interface for the base event class when we wish to query across all event types. This approach gives us the best of both worlds.

Event Repository

We start by creating a repository for the base event type AccountEvent

loading...

Now we simply create a repository interface for each of the event types that we wish to persist...

Account Registered Repository

loading...


Profile Created Repository

loading...


Credential Authentication Request Repository

loading...


Password Changed Repository

loading...


Password Recovery Requested Repository

loading...


As the application evolves, we simple define a new repository interface and add it to the service.

Account History Controller

Now that we have a mechanism that populates our account history, we need a way to access this data. To accomplish this, we create the AccountHistoryController.

loading...

The controller exposes three methods:
  • getPagedHistory- returns a paged list of all events.
  • find- takes an instance of PagedEventHistoryRequest containing the query parameters, and returns a Paged set of results.
  • getEventTypes- returns the set of Account History event types that have been persisted.

Docker Compose

We continue the process of extending our Docker-Compose file by adding our account-history-service.

loading...

Account History Service In Action

We can copy the Docker-Compose file above to your local machine and run it from the command prompt:

 docker-compose -f ./dc-10-account-history.yml up -d

Exercising the Swagger Interface

When all the services have started, navigate to the following url: http://localhost:5010/swagger-ui.html

Account History Swagger UI

Here we see three endpoint methods and the service data models.

Get Paged History

If we expand the GET /all we can see the method form.

Account History Swagger UI- getPagedHistory -form

Clicking on the Try it out button will make the form interactive, and we can supply values to each parameter. Click the Execute button to view the paged results.

Account History Swagger UI- getPagedHistory -result Changing the pageNo value, allows us to page through the results.

Get Event Types

Expanding the GET /eventTypes we can view the method form.

Account History Swagger UI- getEventTypes -form

Clicking on the Try it out button and supply values to each parameter. Then click the Execute button to view the results.

Account History Swagger UI- getEventTypes -result

The service returns an array containing only the event types that have been received. If the application hasn't been exercised completely, some event types may not appear.

Find

Expanding the POST /find we can view the method form.

Account History Swagger UI- find -form

Click the Tryit out button and can supply values to each parameter. Then click the Execute button to view the paged results.

Account History Swagger UI- find -result

The result contains paged results for the query parameters supplied.

Metrics and Monitoring

The Account History Service generates the following service-level metrics:
  • account.history.registration.event.total
  • account.history.authentication.event.total
  • account.history.password.change.event.total
  • account.history.profile.created.event.total
  • account.history.password.recovery.request.event.total
  • account.history.password.recovery.completed.event.total

To visualize this data we will import the AccountHistoryDashboard.json file from the ThinkMicroservices Github Dashboards repository. The dashboard should appear as:

Grafana Account History Dashboard

Resources



Coming Up

In our next article, we will be building the AdministrationService. This service provides several basic administrative functions and demonstrates service proxying and inter-service orchestration.