[BUILD] BFF Pattern with Go Microservices using REST & gRPC.

Oggy
ITNEXT
Published in
5 min readSep 8, 2021

--

High-Level Diagram

Let’s build dockerized Go microservices demonstrating the BFF pattern with REST and gRPC. Bind it to the UI and deploy everything on AWS EC2. (Also has Kotlin & Java versions)

TL;DR: Github Go, Github Kotlin, Github Java, Github UI

BFF, a.k.a. “Best Friends Forever” pattern is a well-established architectural… Jokes aside, the BFF — Back-end for Front-end pattern makes sense when our system has multiple Front-ends that have quite different requirements. Instead of changing our core services based on different Front-end specific demands, we are aiming to fulfill those in a layer that is in between Front-end and core services. So, we are adding a Back-end service for the Front-end. This also allows us to handle such needs in a structured and bounded context.

Ideally, our business/domain-related logic should be placed in microservices that are not exposed to the outside world. Such setup supports SoC, modularity, and flexibility. In this example, User Service and Advice Service will hold our domain logic. BFF Service will be performing gRPC calls to the domain services. Also, if required, it will be responsible for aggregating/customizing the received domain responses.

User Interface (React.js with Semantic UI)

Above, we have our UI. We are going to implement CRUD operations for robot users and link random advice with their user_id. Well, but based on the High-Level diagram we are storing some parts of the data in different repositories. Does this mean we will need to perform multiple network calls in our Front-end? No! BFF Service will handle such scenarios for us. Below, there is a cool gif highlighting the parts that are involved in processing different request types in our example.

The cool gif

So, we now have this extra service that resolves the aforementioned issues. But what about the Performance. The elephant in the room. To reduce newly introduced overhead we will use gRPC between services. Let’s start with defining our contract for the User Service.

user.proto is used to define everything we need in our service. We add all of the required message, service , rpc , enum types here. They will be used by the protoc during code generation.

To generate the related code we can use generate-pb.sh which is basically executing protoc commands.

Great! The bridge between User Service and any other service is now ready. We can start implementing the rpc GetUsers on User Service (gRPC Server).

Above, User Service (gRPC Server) implements the rpc GetUsers by delegating the request to a method in the userdb package which performs DB operations through the mongo_client.

We are done with the User Service (gRPC Server) and ready to start building the BFF Service (gRPC Client). Thanks to protoc , required stub (gRPC Client for the User Service, or any other) is already generated.

After setting up our HTTP/2 connection in the BFF Service, we will use userGrpcServiceClient which is our stub (You can name it any way you like) to communicate. When our stub invokes it’s rpc GetUsers method, the request will reach to our gRPC Server implementation in User Service. And that’s it! We are now able to use MongoDB over gRPC and return the response to BFF Service.

We are almost done, There are just two things to do left for serving our Front-end. Preparing handler functions and run an HTTP server that binds endpoints to our handler functions.

Above, the handler function GetUsers delegates the request to user_client which contains a stub of the User Service named userGrpcServiceClient as we discussed before.

Finally, we will be setting up the HTTP server, we are going to use Gin in this example. gin.Default() attaches Logger and Recovery middleware. cors.Default() allows us to accept traffic from anywhere. We register our endpoints/routes with their handler functions. Now, once a request is made, Gin will invoke the registered handler function.

Hooray! We are done!!! At this point, we can use the repository (MongoDB) of User Service over gRPC, return the response to BFF Service, customize it any way we want, and then return to the Front-end.

The rest of the implementation is available on GitHub. If you are interested in a service that is using SQL repository, check Advice Service (PostgreSQL).

It’s time to dockerize our work! First, we will need a Dockerfile

We should change the "PORT" with the port value that needs to be exposed. For instance,8080. Then, we can build the app and create an image with docker build -t uid4oe/go-ms-bff:latest

Also, I prefer using the scratch as it shrinks the final image size quite a bit. We should apply the same steps for the rest of the services and push images to Docker Hub. docker push uid4oe/go-ms-bff:latest

Finally, we will need a docker-compose.yml file to gather everything. By default, we can use the file in the link. Withdocker-compose.ymlwe should also copy mongo-init.js , advice.sql , .env to our EC2 instance.

Coming to the end, We can follow the Amazon guide for setting up Docker in EC2. Afterward, we need to add docker-compose to our instance using this Gist, and now we are all ready! Oh, make sure inbound rules are configured properly. By default, ports 80 and 8080 should be accepting traffic.

Once we run the commands below all the services will be up and running.

docker network create uid4oe
docker-compose up -d

Wow! that was quite a journey. Thank you very much for your time. All the necessary files are in Github. I’m also adding the Kotlin Spring, Java Spring versions. They are identical to the Go version in terms of functionality. Special thanks to Percy Bolmer for reviewing the story.

Have a nice day!

--

--