DEV Community

Cover image for gRPC Microservices in Go with Hexagonal Architecture – Part 1
SushantDhiman2004
SushantDhiman2004

Posted on • Originally published at sushantcodes.tech

gRPC Microservices in Go with Hexagonal Architecture – Part 1

Hello everyone! Welcome to this ultimate series of microservices with gRPC and Golang. In this series, we are going to discuss everything that you need to know to build microservices using gRPC. Also, we are going to develop everything by following the concept and practices of hexagonal architecture. I will start by simply and keep explaining as we move forward. If you did not understand something keep reading as you will get a better understanding once you have gone through all the content.

The post is originally published on my blog SushantCodes.tech kindly read it there if you want.

What are Remote Procedure Calls (RPC)?

A remote processor call is a protocol that allows us to call a function from our computer on another computer. This is also the best concept of distributed systems. gRPC is a further extended and customized version of RPC that has its benefits. It was developed by Google.

What are distributed systems?

A distributed system is the combination of several computers that are connected in a network and communicate via message passing. For a more realistic example, you can think of it as software being divided into different independent parts and each part being hosted on a separate computer. All the parts and computers communicate via messages to achieve a common goal.

What is a microservices architecture?

Before understanding microservices let’s understand Monolithic. In traditional software environments, all the components of software are hosted on the same server. But the problem arises when a single component fails which leads to the filling of the entire system. So I know the overcome that microservices architecture arises which empowers the concept of dividing software into different parts known as services and hosting it service on a different computer. If any of the services fails then all other services will still work. It is designed in such a way that these services are least interdependent.

Prerequisites: –

For this post, I am assuming that you already know how to make REST APIs because that is the basic concept of backend development. If you don’t know how to make simple APIs, then I would recommend that you first go and learn those concepts.

Aim for This Series:

In this series, we aim to create two different microservices that will be completely independent, and they will communicate with each other using gRPC. Although we can do this thing, we are also using rest architecture, but we want to learn something new. That’s why we are using gRPC.

Let’s Start Coding

Create a new go project by running the following command: –

go mod init order 
Enter fullscreen mode Exit fullscreen mode

I am calling this package as “order” because our one microservice will be for order creation and another microservice will be for payment processing.

Create Folders
Now let us create some folders to organize our code. Below is the oldest structure of hexagonal architecture for microservices. You have to create the same folder and I will explain each of them in detail.

├── cmd
├── config
└── internal
    ├── adapters
    │   ├── db
    │   ├── grpc
    │   └── payment
    ├── application
    │   ├── api
    │   └── domain
    └── ports`
Enter fullscreen mode Exit fullscreen mode

What is Hexagonal Architecture?

Hexagonal Architecture

Hexagonal architecture is the way of designing software by splitting it into two parts. The first part is known as Business Logic or Core and the second part is known as Ports.

  • Core (Business Logic): As its name suggests this part handles all the core logic of the software and fulfills the needs and requirements of the software. This part does not interact with the external world at all.

  • Ports: There are electrical ports in your home and you can plug any appliance and your appliance starts working. But one thing to note here is that your appliance does not need to know what’s inside the port and how everything is working. The same thing happens in hexagonal architecture where ports are the Gateway for external communication. For example, if a database on to communicate with the software or there is a user interface that wants to interact with the software then it is done by ports.

Create Data Structure For Order

Create a new file at internal/application/domain and name it as domain.go. Then add the following code to that.

package domain

import "time"

type OrderItem struct {
    ProductCode string  `json:"product_code"`
    UnitPrice   float32 `json:"unit_price"`
    Quantity    int32   `json:"quantity"`
}

type Order struct {
    ID         int64       `json:"id"`
    CustomerID int64       `json:"customer_id"`
    Status     string      `json:"status"`
    OrderItems []OrderItem `json:"order_items"`
    CreatedAt  int64       `json:"created_at"`
}

func NewOrder(customerID int64, orderItems []OrderItem) Order {
    return Order{
        CustomerID: customerID,
        Status:     "Pending",
        OrderItems: orderItems,
        CreatedAt:  time.Now().Unix(),
    }
}

func (o *Order) TotalPrice() float32 {
    var totalPrice float32
    for _, val := range o.OrderItems {
        totalPrice += val.UnitPrice
    }
    return totalPrice
}
Enter fullscreen mode Exit fullscreen mode

The above defines a simple structure for an order that is being placed by the customer. Think about your Amazon cart while reading this code you will understand automatically. There is a structure “OrderItem” that represents a single order item and another structure “Order” that represents customer details and all the items that he is ordering. Below that we have affection to create new orders and another function to get a total price for the current order.

Create API Interface:

Go to the ports folder create a new file with the name API.go and add the following code to that file.

package ports

import "order/internal/application/domain"

type APIPort interface {
    PlaceOrder(domain.Order) (domain.Order, error)
}
Enter fullscreen mode Exit fullscreen mode

This code is an interface for the API that we are going to build. You will understand the need for this when we move further in this series.

Implementing APIPort

Now we have to implement the interface that we defined above. It is the core logic of our microservice. As we are making an “order” microservice we are going to write some logic to get order requests from the user, save the order in the database, and proceed with payment. This all stuff is a long thing to do so I will keep it short and simple for this part of the series. Currently, my main focus is to get microservices ready and do communication between them. Later we can do some database stuff.

Go to internal/application/api and create a new file “api.go”. Add the following code to this file.

/*
    PoF - This is the core logic of the service.
*/

package api

import (
    "order/internal/application/domain"
    "order/internal/ports"
)

// This struct will implement ports/api.go interface.
type Application struct {}

func NewApplication() *Application {
    return &Application{}
}

func (a Application) PlaceOrder(order domain.Order) (domain.Order, error) {
    fmt.Println("Placing Your Order")

        /*
        Database stuff and communication with Payments Microservice will be done here.
        */

    return order, nil
}
Enter fullscreen mode Exit fullscreen mode

Wrap Up

Okay, now that’s enough for this part I guess. In the next part, we are going to set up gRPC and run this API. Also, we will add database logic to save order. But before that, I would like you to drop a comment if you like this post. It will give me the motivation to create more well-defined content for the next post. Thank you.``

Top comments (0)