Usecases II: Object Mapping
In the first part of the usecases chapter, we looked at the ping usecase which is actually not very useful. It does not receive any parameters and does not return anything. In this chapter, we will create a more realistic scenario in a webservice that offers the multiplication of two integers.
Parameters and Return Values
Let’s start with defining the use case:
public final class MultiplicationUseCase {
public CalculationResponse multiply(final MultiplicationRequest multiplicationRequest) {
final int result = multiplicationRequest.factor1 * multiplicationRequest.factor2;
return calculationResult(result);
}
}
This usecase takes an object of type MultiplicationRequest
as parameter:
public final class MultiplicationRequest {
public final Integer factor1;
public final Integer factor2;
private MultiplicationRequest(final Integer factor1, final Integer factor2) {
this.factor1 = factor1;
this.factor2 = factor2;
}
public static MultiplicationRequest multiplicationRequest(final Integer factor1,
final Integer factor2) {
return new MultiplicationRequest(factor1, factor2);
}
}
As you can see, a MultiplicationRequest
simply encapsulates two factors, each of with
having the datatype Integer
.
The MultiplicationUseCase
will then take both factors, multiply them, and return the result
encapsulated in an object of type CalculationResponse
:
public final class CalculationResponse {
public final Integer result;
private CalculationResponse(final Integer result) {
this.result = result;
}
public static CalculationResponse calculationResult(final Integer result) {
return new CalculationResponse(result);
}
}
The MultiplicationUseCase
is structured in the same way as the PingUseCase
(a public constructor and one public method)
and again does not contain a single dependency on any infrastructure code.
We can now add the usecase to our configuration:
final HttpMaid httpMaid = anHttpMaid()
.post("/multiply", MultiplicationUseCase.class)
.build();
If we would start the application now, a POST
request to /multiply
would fail, because HttpMaid
does not yet know how to create the MultiplicationRequest
parameter and what to do with the CalculationResponse
return value.
Sending a POST
request using curl
$ curl --request POST http://localhost:1337/multiply
would result in a java.lang.NullPointerException
.
Object mapping
Until this point, it is unclear how HttpMaid could get the MultiplicationRequest
parameter needed to call
multiply()
.
One possible and common way to achieve this is to put the input data into the request body
and expect the result to be sent back in the response body.
If we encode the data using Json, an example request body could probably look like this:
{
"factor1": "4",
"factor2": "5"
}
and the corresponding response body would look like this:
{
"result": "20"
}
Now we need a way to reflect this in HttpMaid. As we know from the marshalling chapters, HttpMaid integrates well with its
sister project MapMaid.
We also know that MapMaid is able to (un-)marshall requests and responses i.e. can convert a String
to a Map<String, Object>
and vice versa.
What we didn’t tell you until now is that
it can go even one step further and do conversions from a Map<String, Object>
to domain objects and vice versa.
This additional step is called deserialization and serialization.
Together, MapMaid forms this workflow for unmarshalling (a) and deserialization (b):
Request Body -(a)-> Map<String, Object> -(b)-> Domain Object
and this workflow for serialization (c) and marshalling (d):
Domain Object -(c)-> Map<String, Object> -(d)-> Response Body
Please refer to MapMaid’s documentation if you want to learn more about this feature. Using Gson for marshalling, we end up with a very lean and readable configuration:
final Gson GSON = new Gson();
final HttpMaid httpMaid = anHttpMaid()
.post("/multiply", MultiplicationUseCase.class)
.configured(toMarshallContentType(json(), string -> GSON.fromJson(string, Map.class), GSON::toJson))
.build();
You can try the configuration with the following curl command:
$ curl --request POST --header 'Content-Type: application/json' --data '{"multiplicationRequest": {"factor1": "3", "factor2": "4"}}' http://localhost:1337/multiply
And see the correct result for the multiplication of 3 and 4:
{"result":"12"}