Profile Service

Introduction

Each user that is registered with the application will be provisioned with an Account Profile which allows each user to maintain a both demographic information as well as the user's preferred avatar image. In this article, we will build the Account Profile Service to implement these features.

Requirements

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

Building the Account Profile Service

Account Profile Hexagonal diagram

The AccountProfileService that we are implementing is a simple service. We will however, be introducing MongoDB for data persistence. Unlike the PostgreSQL database we used in the AuthenticationService, MongoDB is schema-less. This gives us the flexibility to evolve the account profile data model to meet the needs of an as-yet-unknown target application. There is no hard requirement that we use a schema-less database for persistence. We will, however, leave the relational implementation as an exercise for the reader.

In addition to providing a REST endpoint, the AccountProfileService provides an event queue message listener. This listener attaches to message bus to listen for AccountRegisteredEvent messages. When AccountRegisteredEvent messages are received, an Account Profile is created in the database.

The source

Maven pom.xml

loading...

Note that we include the spring-boot-starter-data-mongodb dependency which provides support for our MongoDB repository.

Configuration

bootstrap.yml


loading...

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

application.yml

loading...

There are three primary configurations of note here:

  • spring.jackson.serialization.WRITE_DATES_AS_TIMESTAMPS: false-This is set to false since we are using MongoDB.
  • spring.data.mongodb-This section configures the MongoDB's connection information.
  • spring.servlet.multipart-This section configures the min and max sizes for file uploading. This is needed to support avatar uploading in the AccountProfileImageController.

Repositories

The service contains two repositories; ProfileRepository and the ProfileImageRepository.

Profile Repository

The ProfileRepository extends the MongoRepository and supplements the basic CRUD support with three query methods:

loading...

  • findByAccountId-query by account id.
  • findByEmail-query by emaill address.
  • findByQuery-This query searches the first name, middle name, last name, and email address fields for a match. If any of these fields contain the supplied value, it will be included in the Paged response.

Profile Image Repository

We will use the ProfileImageRepostory to store each profile's avatar in the MongoDB. It too, extends the MongoRepository and supplements the CRUD support with the lone findByAccountId method.

loading...

Message Queue Listener

The QueueEventListener binds to the message queue through the ProfileQueueManager class. When messages arrive, their type headers are inspected and routed to the designated method.

loading...

For incoming messages with a header type of Account_Registration_Event, the message will be routed to the processAccountRegisteredEvent. This method will create a new Profile instance and persist it to the database. All other messages will be routed to the trapUnmappedEvent.

Services

The ProfileService wraps both the ProfileRepository and the ProfileImageRepository, as well as the ProfileQueueManager, which are all @Autowired into the service.

loading...

For create and update operations, the service generates corresponding events which are published back into the queue for downstream processing.

Controllers

The AccountProfileService contains two controllers; the AccountProfileController and the AccountProfileImageController.

Account Profile Controller

loading...

This controller provide the REST endpoint for managing the Profile demographic data. It provides support for reading, updating and deleting Account Profiles.

Account Profile Image Controller

loading...

The AccountProfileImageController provides the endpoint to upload and download Account Profile avatar images.

Docker Compose

We continue the process of extending our Docker-Compose file by declaring the account-profile-service.

loading...

Here we include the MongoDB service which will provide persistence service, and we include the Account Profile Service declaration.

Account Profile 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-09-account-profile.yml up -d


Exercising the Swagger Interface

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



In this service, all of the endpoint methods require authorization. To generate a JSON Web Token (JWT) we have two options. The first option it to navigate to the AuthenticationService's Swagger interface ( http://localhost:7777/swagger-ui.html), select the /authenticate option.



Populate the credentials in the request JSON, and click the Execute button.



The response body will include the JWT we need to supply to our AccountProfileService authorization header. While this is certainly a valid approach for generating neccesary authentication tokens, it can be time consuming.

The second approach is to use curl to invoke the AuthenticationService endpoint from a command prompt.

curl -X POST "http://localhost:7777/authenticate" -H "accept: */*" -H "Content-Type: application/json" -d "{ \"email\": \"admin@thinkmicroservices.com\", \"password\": \"Password_1\"}"

This will yield:

{"success":true,"token":"eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJhZG1pbkB0aGlua21pY3Jvc2VydmljZXMuY29tIiwiYWNjb3VudElEIjoiMGNkOGNlODUtZmFmMC00M2M2LWE4MjQtMmZkYWE3MzM1YzAzIiwicmVmcmVzaF90b2tlbiI6ImQ0ZjhjNzgzLTFjZWYtNDBmOC1iMWM3LTBhMDBhYjZiMjUxYyIsInJvbGVzIjpbImFkbWluIiwidXNlciJdLCJpc3MiOiJUaGlua01pY3Jvc2VydmljZXMuY29tIiwiZXhwIjoxNTg1NzAzMzQyLCJpYXQiOjE1ODU3MDMyODIsInJlZnJlc2hfdG9rZW5fZXhwIjoxNTg1NzA0MTgyNjc3fQ.G_z2cO-C5y1xOoOPhGWHe2N1kRuTmOxfd793HDpIiB6PkC_h_tA0-1-_Q2v4liStp2_GgbHNo-dwiwZS7IOO-A","errorMessage":null}
( NOTE: The value of the token will be different )

This approach can be faster, especially if run from a simple shell script. When exercising secured REST interfaces, we will need a source of valid, non-expired authentication token.

We will exercise the Swagger UI by seleting the /profile/all option. This method will return a paged listing of all profiles that match the like field regex value.



We will use the default values for every field except the Authorization. In this field we supply the authorization token we received and we prefix it with the bearer string and a single space. When we click the Execute button, we will see the following.



Here we see a partial response displayed in the Swagger UI. The full JSON response is:


  {
  "content": [
  {
    "id": "5e83c5a3048be40001b4c73c",
    "accountId": "0cd8ce85-faf0-43c6-a824-2fdaa7335c03",
    "email": "admin@thinkmicroservices.com",
    "firstName": "Admin",
    "lastName": "Strator",
    "middleName": "I",
    "primaryStreetAddress": null,
    "secondaryStreetAddress": null,
    "city": null,
    "state": null,
    "postalCode": null,
    "phone": null,
    "dob": null
  }
  ],
  "pageable": {
  "sort": {
  "sorted": true,
  "unsorted": false,
  "empty": false
},
"pageSize": 10,
"pageNumber": 0,
"offset": 0,
"unpaged": false,
"paged": true
},
"totalPages": 1,
"totalElements": 1,
"last": true,
"numberOfElements": 1,
"sort": {
"sorted": true,
"unsorted": false,
"empty": false
},
"first": true,
"size": 10,
"number": 0,
"empty": false
}

The JSON response is in the form:



Here we see that the paged response has a single profile. To edit this profile we return to the top-level options and select the PUT /profile/account/{id}option. To exercise this option click the Try it out button.



Here we supply a valid Authorization token, the id if the profile we want to update, and we supply the request JSON with the primary street address, city, state, and postal code field values reflecting the changes we wish to make.



We can verify that our updates by returning to the top-level options and select the GET /profile/account/{id} option.

Get Account Profiles Form

We supply a valid authentication token, the account id, and click Execute.

Swagger-Get response

The JSON response confirms that our changes have been persisted.

Metrics and Monitoring

The Account Profiles Service generates the following service-level metrics:
  • account.profile.created.total
  • account.profile.updated.total
  • account.profile.deleted.total
This will allow us to monitor profile creation, updates and deletes that are being by the service.

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

Grafana Account Profiles Dashboard

Resources



Coming Up

In our next article, we will be implementing the AccountHistoryService. This service listens for, and records, all account-related events. It also provides a REST endpoint that allows us to query the event history.