Inventory BFF
Develop and deploy the BFF component of the inventory application
Setup
The Inventory BFF’s role in the architecture is to act as an orchestrator between the core business services and the specific digital channel it is focused on supporting. This class article will give you more detail about the architectural pattern and the benefits. Backend for Frontend
The Inventory solution will use GraphQL for its BFF layer, which enables the API to be dynamically controlled from the client using API queries. Follow the steps below to get started.
Setup
To get the initial BFF project created and registered with a pipeline for automated builds follow these steps.
Create a new repository from the Typescript GraphQL Code Pattern
- In order to prevent naming collisions, name the repository
inventory-management-bff-{your initials}
replacing{your initials}
with your actual initials.
- In order to prevent naming collisions, name the repository
Clone the new repository to your machine
Run
npm install
to install all the package dependenciesGo into the repository directory cloned and execute the following
oc sync dev-{your initials} --devRegister the pipeline register the pipeline
oc pipeline --tektonreplacing
{your initials}
with your actual initialsOpen the pipeline to see it running
When the pipeline is completed, run
oc endpoints -n dev-{your initials}
. You should see an entry for the app we just pushed. Select the entry and hitEnter
to launch the browser, if you are working on your desktop/laptop. Otherwise copy the url and paste it in a new browser tab.
Build the controller for the REST interface
The controller provides the REST interface for our BFF. The Code Pattern uses the typescript-rest
package to simplify the tasks required to create a controller.
- Start the tests in tdd mode by running
npm run tdd
- Create the controller test
test/controllers/stock-items.controller.spec.tsimport {Application} from 'express';import * as request from 'supertest';import {buildApiServer} from '../helper';describe('stock-item.controller', () => {let app: Application;beforeEach(async () => {
- Create the controller component
src/controllers/stock-items.controller.tsimport {GET, Path} from 'typescript-rest';@Path('stock-items')export class StockItemsController {@GETasync listStockItems(): Promise<any[]> {return [];}
- Add the controller to the controllers
index.ts
. (Usingindex.ts
is a good way to manage which components are exposed by a component and provide a good way to load the modules that will be injected into other components)
src/controllers/index.tsexport * from './health.controller';export * from './stock-items.controller';
- Start the service to see it running
npm start
- Click on open link
- To view this application in new tab click top right corner arrow icon
To view the running app click on the Eye Icon on the top right and select the port
3000
this will open a browser tab and display the running app on that port.
Expand our service from the list, click
Try it out
, then clickExecute
Push the changes we’ve made to the repository
git add .git commit -m "Adds stock items controller"git push
- Open the pipeline to see it running
Update the controller to call a service
The pattern recommended for the REST controllers is to let it focus on translating REST protocols into javascript and to put the business logic in a separate service component.
- Add a StockItem model that contains the values needed for the UI
src/models/stock-item.model.tsexport class StockItemModel {id: string;name: string;description: string;stock: number;unitPrice: number;picture: string;manufacturer: string;}
- Register the model with the
index.ts
file in the models directory. Append this to end of the file.
src/models/index.ts...export * from './stock-item.model';
- Define an abstract class to provide the interface for our API
src/services/stock-items.api.tsimport {StockItemModel} from '../models';export abstract class StockItemsApi {async abstract listStockItems(): Promise<StockItemModel[]>;}
- Add the abstract class to the
index.ts
file in the services directory. Add it to the end of other export statements, do not overwrite the file.
src/services/index.ts...export * from './stock-items.api';...
- Update the controller test to inject the service into the controller and to return the value from the service
test/controllers/stock-items.controller.spec.tsimport {Application} from 'express';import * as request from 'supertest';import {Container} from 'typescript-ioc';import {buildApiServer} from '../helper';import Mock = jest.Mock;import {StockItemsMockService} from '../../src/services';describe('stock-item.controller', () => {
- Update the controller to inject the service and use it
src/controllers/stock-items.controller.tsimport {Inject} from 'typescript-ioc';import {GET, Path} from 'typescript-rest';import {HttpError} from 'typescript-rest/dist/server/model/errors';import {StockItemModel} from '../models';import {StockItemsMockService} from '../services';class BadGateway extends HttpError {constructor(message?: string) {
Create a mock service implementation
Now that we have our Controller using our API to get the data, lets create an implementation that will provide mock data for now.
- Add a
stock-items-mock.service
to services
src/services/stock-items-mock.service.tsimport {StockItemsApi} from './stock-items.api';import {StockItemModel} from '../models';export class StockItemsMockService implements StockItemsApi {async listStockItems(): Promise<StockItemModel[]> {return [{id: "1",
- Add the mock service to the
index.ts
file in the services directory
src/services/index.ts...export * from './stock-items-mock.service';...
- Start the service
npm start
- Click on open link
- To view this application in new tab click top right corner arrow icon
To view the running app click on the Eye Icon on the top right and select the port
3000
this will open a browser tab and display the running app on that port.
- Push the changes we’ve made to the repository
git add .git commit -m "Adds a mock service implementation"git push
- Open the pipeline to see it running
Add a GraphQL implementation of Stock Items
The GraphQL Code Pattern supports both REST and GraphQL APIs for accessing backend services. We created a REST controller to expose the results from the service and now we will do the same for GraphQL.
- Create a
stock-items
GraphQL schema in theschemas
directory
src/schemas/stock-item.schema.tsimport {Field, Float, Int, ObjectType} from 'type-graphql';import {StockItemModel} from '../models';@ObjectType()export class StockItem implements StockItemModel {@Field()id: string;@Field()description: string;
- Add the stock-items schema to the
index.ts
in the schemas directory
src/schemas/index.tsexport * from './stock-item.schema'
- Add a ‘stock-item’ GraphQL resolver in the
resolvers
directory
src/resolvers/stock-item.resolver.tsimport {Query, Resolver} from 'type-graphql';import {Inject} from 'typescript-ioc';import {resolverManager} from './_resolver-manager';import {StockItem} from '../schemas';import {StockItemModel} from '../models';import {StockItemsMockService} from '../services';@Resolver(of => StockItem)
- Add the stock-items resolver to
index.ts
in the resolvers directory
src/resolvers/index.tsexport * from './stock-item.resolver';
- Start the service
npm start
- Verify that the that the resolver is available using the Graph QL browser provided by the Code Pattern
- Open GraphQL Playground:
http://localhost:3000
- Run the query
query { stockItems { name } }
- Open GraphQL Playground:
- Click on open link
- To view this application in new tab click top right corner arrow icon
To view the running app click on the Eye Icon on the top right and select the port
3000
this will open a browser tab and display the running app on that port.
- Push the changes we’ve made to the repository
git add .git commit -m "Adds a graphql interface"git push
- Open the pipeline to see it running
Create a service implementation that calls the microservice
Create a folder
config
in following pathsrc/config
Add a
stock-item-service.config
file in the config directory
src/config/stock-item-service.config.tsexport class StockItemServiceConfig {baseUrl: string;}
- Add a
stock-item-service.config.provider
file in the config directory
src/config/stock-item-service.config.provider.tsimport {ObjectFactory} from 'typescript-ioc';const baseUrl: string = process.env.SERVICE_URL || 'localhost:9080';export const stockItemConfigFactory: ObjectFactory = () => ({baseUrl,});
The config class separates how the config is loaded from how it is used. In this case the config is simply retrieved from an environment variable but in more complex cases the value(s) can be retrived from external data sources.
- Add the
stock-item-service.config
to an index.ts of the config directory
src/config/index.tsimport {StockItemServiceConfig} from './stock-item-service.config';import {stockItemConfigFactory} from './stock-item-service.config.provider';import {Container} from 'typescript-ioc';export * from './stock-item-service.config';Container.bind(StockItemServiceConfig).factory(stockItemConfigFactory);
- Create a
stock-items
service in the services directory that uses the config
src/services/stock-items.service.tsimport {Inject} from 'typescript-ioc';import {get, Response} from 'superagent';import {StockItemsApi} from './stock-items.api';import {StockItemModel} from '../models';import {StockItemServiceConfig} from '../config';import {LoggerApi} from '../logger';class StockItem {
- Add
stock-item.service
toindex.ts
in the service directory - Add
StockItemsService
instead ofStockItemsMockService
in the following filessrc/resolvers/stock-item.resolver.ts
,src/controllers/stock-items.controller.ts
andtest/controllers/stock-items.controller.spec.ts
- Remove
stock-items-mock.service
fromindex.ts
src/services/index.tsexport * from './stock-items.service';
- Modify
connectsTo
property to the values.yaml file of the Helm chart. The value of the property should match the Kubernetes service of the microservice. (For Code Pattern projects, the service name is the same as the name of the application which is that same as the name of the repository.)
chart/base/values.yaml...connectsTo: inventory-management-svc-{your initials}...
The values.yaml
file of the Helm chart defines the variables that can be provided to the
template as input. Now that we’ve added a new variable, we will need to update the appropriate
template file to use our new variable.
- Add a new environment variable named
SERVICE_URL
to the list of existing environment variables in deployment.yaml. (SERVICE_URL
is the name we gave the environment variable in ourstock-item-service.config
class as the first step in this section.) The value of this environment variable should come from theconnectsTo
value we defined. You can add| quote
to wrap the value in quotes in case the value is not formatted correctly.
chart/base/templates/deployment.yaml...env:- name: INGRESS_HOSTvalue: ""- name: PROTOCOLSvalue: ""- name: LOG_LEVELvalue: {{ .Values.logLevel | quote }}- name: SERVICE_URL
deployment.yaml
is a templatized Kubernetes yaml file that describes the deployment of our component.
The deployment will create one or more pods based on the pod template defined in the deployment.
Each pod that starts will have the envionment variables that we have defined in the env
section
available for the container image to reference.
- Commit and push the changes to git
git add .git commit -m "Adds service implementation"git push
- Open the pipeline to see it running