Creating the Application Service¶
On the previous article we learned how to expose a query from the inside to the outside using the Backend for Frontend. This approach works great for read operations, but what about writing, a.k.a. creation and update of entities?
Write operations in GraphQL are done using Mutations, which are very similar to Queries. That being said, the microservices themselves do not provide mutations as all creations and updates have to be done using messages over the bus.
Those messages are produced by the Application services, which implements the
use cases that the application needs. We are going to introduce a new service on
the EMA
bounded context that will be in charge of exposing GraphQL Mutations
for our equipment entity and translate them into messages for our Equipments
microservice.
Creating the service¶
We are going to use the suiteaspnetapp
template to create the application
service. To do so, run the following command:
Important
Don't forget to activate the console with the source activate.sh
command.
Bash | |
---|---|
This template doesn't require any special parameters, just the name and the
output directory. After running the command we need to close and reopen Visual
Studio using the slngen.sh
script, after doing so, we should see the newly
created project inside the EMA bounded context.
The first thing were are going to do is to rename the SuiteWebAppModule
to
EmaApplicationModule
, remember to rename both the class and the file.
Next we need to reference the GraphQLModule
. Open the
ITsynch.Suite.EMA.Application
project file and add the following project
reference:
XML | |
---|---|
Creating the DTOs¶
In order to be able to create and update equipments we need to create DTOs for
both operations. By convention, we store input and output types on Contracts
projects. So lets create a new project called
ITsynch.Suite.EMA.Application.Contracts
on the EMA bounded context using the
suitemodule
template:
Bash | |
---|---|
As usual we need to reopen the solution with ./slngen.sh
to see the newly
created project.
Now lets delete de default Module.cs
file and lets create a new folder called
GraphQL
. Inside that folder add a new class called CreateEquipment
as
follows:
This is the shape the the client will need to send us to create a new Equipment. It doesn't have a correlation ID because that field is generated by the service, not the client.
Now lets add another class for updating an equipment:
We do need the CorrelationId here so we know which entity to update.
Important
Both classes are public. Also pay attention to the namespace.
Exposing the message as mutations¶
As we mentioned on the introduction, we need to somehow publish a message based on a GraphQL mutation, thankfully the Suite Framework provides a way to do this declaratively: the Exposed Message as Mutation feature.
In our EMA.Application
project, we need to get a reference to the message that
we need to produce in order to create an Equipment, so we are gonna reference
the Equipments.Contracts
project.
We also need to add a reference to the EMA.Application.Contracts
project we
created on the section above so that we have access to the GQL objects we
declared. We will also need a reference to the MassTransitModule
.
This is how the EMA.Application
project should look like now:
Now lets override the SetupModule
method on our EmaApplicationModule
. Inside
it we are going to depend on the MassTransitModule
and on the GraphQLModule
and use the GraphQLModuleOptions
to add some configuration:
C# | |
---|---|
The GraphQLModuleOptions
class provides a method call
ExposedMessageAsMutation
that we can use to expose a mutation with a given DTO
that is going to be translated into a message and then sent to the bus when the
mutation is called:
The first generic type parameter, GraphQL.CreateEquipment
, is the input DTO
that will be exposed for the UI to produce.
The second one, CreateOrUpdateEquipment
, is the message that is going to be
sent when the mutation is executed.
The third one, EquipmentUpdated
, is the response that the consumer sends if
the creation is successful.
The "createEquipment"
parameter is the name of the mutation and the second one
is a builder expression that allows us to do some configuration.
For the creation case, when we produce the CreateOrUpdateEquipment
, we want to
generate a NewId
in the CorrelationId
field. The UseIdField
does just
that: it receives an expression that returns a field which will get a NewId
set.
Now lets add a similar configuration for the update operation:
C# | |
---|---|
As you can see, it is very similar, but simpler, as we do not need to use the
builder, because no GUID is going to be generated. The CorrelationId is part of
the UpdateEquipment
message, because the client needs to tell the service what
equipment should be updated.
Setting up AutoMapper¶
As we mentioned, with the configuration done on the previous section the
framework will automatically translate the CreateEquipment
and
UpdateEquipment
DTOs into the CreateOrUpdateEquipment
message. But how does
it know how to translate from one object to the other?
It uses AutoMapper, so to finish our setup we need to add the corresponding
profiles for it. Lets start by adding the AutoMapper module to our
EMA.Application
project:
XML | |
---|---|
Then lets add a folder called AutoMapperProfiles
on our EMA.Application
project, and inside it, a new class called EquipmentProfile
. This class must
inherit from Profile
and in its constructor we will define the mappings for
both DTOs.
Both mappings are quite similar, the only difference is that on creation one we
tell AutoMapper not to worry about the CorrelationId property, as the framework
will fill it in (this is related to the usage of UseIdField
when exposing the
message). For the update we map the CorrelationId as usual, since we want to use
the CorrelationId sent by the client.
If we try to execute our service now, we will run into a problem. The
CreateOrUpdateEquipment
record has no parameterless constructor, so AutoMapper
will not be able to create it. We need to modify it:
Important
By changing the CreateOrUpdateEquipment record you will also need to change the Tests we created on a previous article, in the places where the record is instantiated, to use an object initializer instead of the constructor. Since these changes are trivial, they are omitted.
Publishing the schema to the BFF¶
As we mentioned in previous articles the only entrance point to the ecosystem is
through the BFF, so we cannot directly called the mutations we declared, we need
to publish them to the correct federation, as we did with the query from the
Equipments
microservice. To do so, lets add a federations.json
file at the
root of our EMA.Application
project as follows:
JSON | |
---|---|
We also need to tell the BFF how to find the service to forward the calls. On
the appsettings.json
file we need to add the entry for the Service Discovery
and the federation:
Since we have set the port 22010
for the new service, we need to modify the
launchSettings.json
file on the EMA.Application
project to use it:
JSON | |
---|---|
Running all¶
Now we should be able to start the 3 processes, the Equipments
microservice,
the EMA.Application
service and the EMA.Bff
. On the playground of the
EMA.Bff
we should be able to create a new equipment as follows:
We can run the following query to check that new equipment was created:
And we can see the logs of the BFF, Application, and backend service to see what happened.
You can now grab the CorrelationId of the new equipment and try to create an
updateEquipment
mutation.