HSG-MCS-HS21_tapas/tapas-tasks/README.md

10 KiB

tapas-tasks

Micro-service for Managing Tasks in a Task List implemented following Hexagonal Architecture.

Based on examples from book "Get Your Hands Dirty on Clean Architecture" by Tom Hombergs

Technologies: Java, Spring Boot, Maven

Note: this repository contains an EditorConfig file (.editorconfig) with default editor settings. EditorConfig is supported out-of-the-box by the IntelliJ IDE. To help maintain consistent code styles, we recommend to reuse this editor configuration file in all your services.

HTTP API Overview

The code we provide includes a minimalistic uniform HTTP API for (i) creating a new task, (ii) retrieving a representation of the current state of a task, and (iii) patching the representation of a task, which is mapped to a domain/integration event.

The representations exchanged with the API use two media types:

  • a JSON-based format for task with the media type application/task+json; this media type is defined in the context of our project, but could be registered with IANA to promote interoperability (see TaskJsonRepresentation for more details)
  • the JSON Patch format with the registered media type application/json-patch+json, which is also a JSON-based format (see sample HTTP requests below).

For further developing and working with your HTTP API, we recommend to use Postman.

Creating a new task

A new task is created via an HTTP POST request to the /tasks/ endpoint. The body of the request must include a representation of the task to be created using the content type application/task+json defined in the context of this project. A valid representation must include at least two required fields (see TaskJsonRepresentation for more details):

  • taskName: a string that represents the name of the task to be created
  • taskType: a string that represents the type of the task to be created

A sample HTTP request with curl:

curl -i --location --request POST 'http://localhost:8081/tasks/' \
--header 'Content-Type: application/task+json' \
--data-raw '{
  "taskName" : "task1",
  "taskType" : "computation",
  "originalTaskUri" : "http://example.org",
  "inputData" : "1+1"
}'

HTTP/1.1 201
Location: http://localhost:8081/tasks/cef2fa9d-367b-4e7f-bf06-3b1fea35f354
Content-Type: application/task+json
Content-Length: 170
Date: Sun, 17 Oct 2021 21:03:34 GMT

{
  "taskId":"cef2fa9d-367b-4e7f-bf06-3b1fea35f354",
  "taskName":"task1",
  "taskType":"computation",
  "taskStatus":"OPEN",
  "originalTaskUri":"http://example.org",
  "inputData":"1+1"
}

If the task is created successfuly, a 201 Created status code is returned together with a representation of the created task. The response also includes a Location header filed that points to the URI of the newly created task.

Retrieving a task

The representation of a task is retrieved via an HTTP GET request to the URI of task.

A sample HTTP request with curl:

curl -i --location --request GET 'http://localhost:8081/tasks/cef2fa9d-367b-4e7f-bf06-3b1fea35f354'

HTTP/1.1 200
Content-Type: application/task+json
Content-Length: 170
Date: Sun, 17 Oct 2021 21:07:04 GMT

{
  "taskId":"cef2fa9d-367b-4e7f-bf06-3b1fea35f354",
  "taskName":"task1",
  "taskType":"computation",
  "taskStatus":"OPEN",
  "originalTaskUri":"http://example.org",
  "inputData":"1+1"
}

Patching a task

REST emphasizes the generality of interfaces to promote uniform interaction. For instance, we can use the HTTP PATCH method to implement fine-grained updates to the representational state of a task, which may translate to various domain/integration events. However, to conform to the uniform interface contraint in REST, any such updates have to rely on standard knowledge — and thus to hide away the implementation details of our service.

In addition to the application/task+json media type we defined for our uniform HTTP API, a standard representation format we can use to specify fine-grained updates to the representation of tasks is JSON Patch. In what follow, we provide a few examples of HTTP PATCH requests. For further details on the JSON Patch format, see also RFC 6902).

Changing the status of a task from OPEN to ASSIGNED

Sample HTTP request that assigns the previously created task to group tapas-group1:

curl -i --location --request PATCH 'http://localhost:8081/tasks/cef2fa9d-367b-4e7f-bf06-3b1fea35f354' \
--header 'Content-Type: application/json-patch+json' \
--data-raw '[ {"op" : "replace", "path": "/taskStatus", "value" : "ASSIGNED" },
  {"op" : "add", "path": "/serviceProvider", "value" : "tapas-group1" } ]'

HTTP/1.1 200
Content-Type: application/task+json
Content-Length: 207
Date: Sun, 17 Oct 2021 21:20:58 GMT

{
  "taskId":"cef2fa9d-367b-4e7f-bf06-3b1fea35f354",
  "taskName":"task1",
  "taskType":"computation",
  "taskStatus":"ASSIGNED",
  "originalTaskUri":"http://example.org",
  "serviceProvider":"tapas-group1",
  "inputData":"1+1"
}

In this example, the requested patch includes two JSON Patch operations:

  • an operation to replace the taskStatus already in the task's representation with the value ASSIGNED
  • an operation to add to the task's representation a serviceProvider with the value tapas-group1

Internally, this request is mapped to a TaskAssignedEvent. The HTTP response returns a 200 OK status code together with the updated representation of the task.

Changing the status of a task from to EXECUTED

Sample HTTP request that changes the status of the task to EXECUTED and adds an output result:

curl -i --location --request PATCH 'http://localhost:8081/tasks/cef2fa9d-367b-4e7f-bf06-3b1fea35f354' \
--header 'Content-Type: application/json-patch+json' \
--data-raw '[ {"op" : "replace", "path": "/taskStatus", "value" : "EXECUTED" },
  {"op" : "add", "path": "/outputData", "value" : "2" } ]'

HTTP/1.1 200
Content-Type: application/task+json
Content-Length: 224
Date: Sun, 17 Oct 2021 21:32:25 GMT

{
  "taskId":"cef2fa9d-367b-4e7f-bf06-3b1fea35f354",
  "taskName":"task1",
  "taskType":"computation",
  "taskStatus":"EXECUTED",
  "originalTaskUri":"http://example.org",
  "serviceProvider":"tapas-group1",
  "inputData":"1+1",
  "outputData":"2"
}

Internally, this request is mapped to a TaskExecutedEvent. The HTTP response returns a 200 OK status code together with the updated representation of the task.

Working with MongoDB

The provided TAPAS Tasks service is connected to a MongoDB as a repository for persisting data.

Here are some pointers to start integrating the MongoDB with the other microservices:

  • application.properties defines the
    • URI of the DB server that Spring will connect to (mongodbservice running in Docker container). Username and password for the server can be found in docker-compose.yml.
    • Name of the database for the microservice (tapas-tasks)
  • docker-compose.yml defines
    • in lines 74-82: the configuration of the mongodb service based on the mongodb container including the root username and password (once deployed this cannot be changed anymore!)
    • in lines 84-102: the configuration of a web application called mongo-express to manage the MongoDB server. The web app can be reached via the URI: http://dbadmin.${PUB_IP}.nip.io. Login credentials for mongo-express can be found in lines 89 and 90.
    • in lines 104-105: the volume to be used by the mongodb service for writing and storing data (do not forget!).
  • The pom.xml needs to have spring-boot-starter-data-mongodb and spring-data-mongodb as new dependencies.
  • The TapasTasksApplication.java specifies in line 9 the location of the MongoRepository classes for the microservice.
  • The persistence.mongodb package has the relevant classes to work with MongoDB:

General hints:

  • To not overload the VMs we recommend to use only one MongoDB server that all microservices connect to. Per microservice you could use one database or one collection (discuss in your ADRs!). To use more than one MongoDB server you have to extend the docker-compose.yml file by basically replicating lines 74-105 and changing the names of the services and volumes to be unique (ask your tutors!).
  • For local testing you have to install the MongoDB server locally on your computers and change the spring.data.mongodb.uri String in application.properties.