Skip to content

Creating the Equipment Management App BFF

So far we have created the Equipments microservice with its own Equipment entity, including a GraphQL query to get them. However our service cannot be accessed from outside the ecosystem. This is by design, microservices live in an isolated ecosystem, where they can communicate with each other using the message bus, but they cannot receive nor send any message or call to the outside world. All external communication must be handled by a Backend For Frontend (BFF) service.

We do not create a BFF for each microservice, instead, we create one for each Application or Frontend that we have (hence the Frontend on the name). For example we have the Admin Center BFF.

The responsibility of the BFF are:

  1. Cohesively integrate multiple services to create an API surface suited for a given Frontend
  2. Handle the transport layer security (certifications, etc.)
  3. Handle Authentication/Authorization

We are going to create an Equipment Management BFF so we can later create a Frontend application for managing Equipments.

Creating the Backend for Frontend Project

To begin with the BFF creation we are going to use a template. As we mentioned before, the BFF is for the frontend, hence it is named after the UI application instead of any services it wraps.

For the purpose of this tutorial, our app will be called Equipments Management App (EMA).

Lets run the following on a bash compatible console, on the dotnet directory:

Important

Don't forget to activate the console with the source activate.sh command.

Bash
dotnet new bff -n EMA --BoundedContext EMA --EntityName Equipment
  • bff: this is the name of the BFF template.
  • -n: the name for our BFF.
  • --BoundedContext: the bounded context for our BFF. We create a new one for the EMA (Equipment Management App). We do this, because usually applications include other services to implement the features the frontend needs.
  • --EntityName: an entity name to be use to setup some sample code on the newly created BFF.

Lets check what the template has created for us by running:

Bash
./slngen.sh EMA

The solution should look like this:

BFF newly created

As you can see we have an executable project for our service, the ITsynch.Suite.EMA.Bff and an Suite Module for UI Localization, this one includes the localization resources that our UI application will use.

Note

We try to configure the BFF and let the BFF provide the config to the UI, so that the UI has no configuration at all: we configure "the app" (both bff and UI) through the BFF's appsettings.json Check the SpaConfiguration module if you wanna learn more about this.

Our microservice is not included as it belongs to another Bounded Context. But since we are going to be working on both the microservice and the BFF it is a good idea to include the microservice on the .tvproj file of our EMA bounded context so that we can work on them both in the same VS instance.

To do so, open the /dotnet/src/services/EMA/ITsynch.Suite.EMA.tvproj and add the following project references below the existing ones:

XML
<ProjectReference Include="$(ServicesPath)Equipments/**/*.*proj" />
<ProjectReference Include="$(TestsPath)services/Equipments/**/*.*proj" />

Now close and re-open the EMA Bounded Context with ./slngen.sh and we should see de Equipments projects included on our solution.

Exposing the Equipments query to the outside world

As we mentioned, the equipments query from the Equipments microservice cannot be access directly form outside the ecosystem. The BFF is in charge of acting as a proxy for it. To achieve this the BFF uses a concept called Schema Federation. To setup federation we need 2 configurations:

  1. On the microservice, publish its schema to a federation.
  2. On the BFF, declare the name of the federation it represents.

Publishing the Equipments schema to the EMA federation

To publish the Equipments schema we need to add a new file called federations.json on the ITsynch.Suite.Equipments.Application project. Inside the file we should write the following configuration:

Important

The name is important as it is read by convention.

JSON
{
    "GraphQLModuleOptions": {
        "Federations": [
            {
                "SchemaName": "equipments",
                "FederationName": "EMA",
                "IgnoreRootTypes": true,
                "SchemaExtensionFiles": ["EMA.extensions.graphql"]
            }
        ]
    }
}
  • Federations: is a list, because a microservice can publish its schema to many federations.
  • SchemaName: the name the schema will have on the federation. We can use the name of the microservices, or we can change it, for example, to avoid collisions.
  • FederationName: the name of the federation where we want to register to. This name will be used by the BFF to search for the published schemas. We generally used the same as the frontend, when possible.
  • IgnoreRootTypes: this tells the microservice not to publish all the GraphQL fields. We want to explicitly control what fields are published. See this.
  • SchemaExtensionFiles: the list of file where we define what is going to be published.

Now we need to create a new file called EMA.extensions.graphql on the root of our microservice project. Inside it we need to extend the Query type and add the equipments field. The easiest way is to run our microservice and access the playground. There we can check the schema definition, we need to fine the equipments query definition and copy it and paste it on the EMA.extensions.graphql. After doing so we need to apply 2 changes:

  1. Add the extend keyword.

  2. Add the @delegate() directive.

GraphQL
extend type Query {
  equipments(
    first: Int
    after: String
    last: Int
    before: String
    where: EquipmentInput
    order: [EquipmentSortInput!]
  ): EquipmentsConnection @delegate()
}

With this setup we are telling the federation that we want to publish a field called equipments on the BFF that when call will be redirected to the equipments field on the microservice. Please read this article for a more thorough explanation.

Consuming the Schema on the BFF

Now that the microservice is ready to publish the schema, we need to setup our BFF to consume it. On the appsettings.json file of our BFF we are going to find the following:

JSON
"ServiceDiscoveryDefaultProviderOptions": {
    "services": {
        "backend-for-frontend": {
            "addresses": ["localhost"]
        }
    }
},

"GraphQLGatewayModuleOptions": {
    "RemoteSchemas": {
        "EMA": "backend-for-frontend"
    },
    "FederationName": "EMA"
},

The template has left some placeholder for us. The FederationName is already set, but we need to change the backend-for-frontend text with the actual name for our microservice, and also set a correct address for it. We are going to use equipments-service for the name, and 22009 for the port. Also we need to set the correct schema name in the RemoteSchemas list, in this case equipments.

Important

When creating a real service, discuss with the Suite Framework team what name and port to be used.

JSON
"ServiceDiscoveryDefaultProviderOptions": {
    "services": {
        "equipments-service": {
            "addresses": [ "http://localhost:22009" ]
        }
    }
},

"GraphQLGatewayModuleOptions": {
    "RemoteSchemas": {
        "equipments": "equipments-service"
    },
    "FederationName": "EMA"
},

We need to tell the BFF to register the default root types so the microservice can extend them. Fortunately this is already included by the template, you can check the EMABffModule, it should contain the following:

C#
1
2
3
4
builder.DependsOn<GraphQLGatewayModule, GraphQLGatewayModuleOptions>(opts =>
{
    opts.RegisterRootTypes = true;
});

Finally we need to go back to our ITsynch.Suite.Equipments.Application project and change the launch settings on the launchSettings.json file so our microservice start on the assigned port 22009.

JSON
{
    "profiles": {
        "ITsynch.Suite.Equipments.Application": {
            "commandName": "Project",
            "launchBrowser": true,
            "launchUrl": "graphql",
            "applicationUrl": "http://localhost:22009",
            "environmentVariables": {
                "ASPNETCORE_ENVIRONMENT": "Development"
            }
        }
    }
}

Now we should be able to run the service and the BFF. First we need to start the Equipments microservice and then the EMA BFF. We should see the equipments query on the GraphQL playground of the BFF.

You can start both projects at the same time by using right clicking on the Solution, Run Multiple Projects, and choosing both the BFF and the Equipments.Application.

If we now browse to http://localhost:22009/graphql we should see our BFF's GQL endpoint with our equipments query.