DEV Community

Cover image for Building RESTful API with Hexagonal Architecture in Go
Bagas Hizbullah
Bagas Hizbullah

Posted on • Edited on

Building RESTful API with Hexagonal Architecture in Go

I've been learning how to build web applications using different frameworks and languages for a while now, such as Laravel with its MVC architecture and Node.js following the 'Hapi.js Way'. As I'm trying to create a new portfolio project using Go, I found myself contemplating over the ideal project structure. I wanted something that not only aligns with the standard Go project layout, but also makes the code both easy to write and understand. That's when I stumbled upon the concept of Hexagonal Architecture, as showcased in Netflix's engineering blog. The idea of seamlessly swapping infrastructures with minimal code changes fascinated me, and I decided to implement it in my new project.

Now, the remaining question was, "what should I build?" Fortunately, as I was casually scrolling through YouTube videos, I stumbled upon this hidden gem video titled 'Ide Project untuk Upgrade Portfolio Backend Engineer' by Asdita Prasetya. He recommended developing a RESTful Point of Sale API service. It felt like the perfect project to put my newfound architectural knowledge into practice. So, getting my coffee and keyboard ready, I started on this exciting learning journey!

Hexagonal Architecture

Create your application to work without either a UI or a database so you can run automated regression-tests against the application, work when the database becomes unavailable, and link applications together without any user involvement.

Hexagonal Architecture, also known as Ports & Adapters Architecture, is one of several ways to build decoupled software systems. It was popularized by Alistair Cockburn, who is known as one of the initiators of the agile movement in software development. This way of organizing software is great for making applications that are easy to work on and can change without breaking.

The main idea behind Hexagonal Architecture is to separate the application's core business logic from the external stuff, such as databases, user interfaces, and other external services. It promotes a clean and modular structure that makes it easier to test, maintain, and scale the application.

Hexagonal Architecture

Core

The core is where the heart of the application lives. It contains the essential business logic and rules of the application. This is where things like processing orders, managing user accounts, and performing all the tasks of the application is designed for. The core should be independent of any external technologies or frameworks, making it highly portable and reusable.

There are two other essential concepts in Hexagonal Architecture: "ports" and "adapters". These concepts dictates how the "core" interacts with the external components.

Ports

Think of ports as contracts or interfaces. They define how the application communicates with external systems or services. For example, a port could specify the rules for connecting to a database, interacting with another web services, or handling user interfaces. Ports belongs to the core, because the core defines which actions are required to achieve the business logic goals.

Adapters

Adapters are the ones who implement the contracts or interfaces defined by ports. Adapters are responsible for making sure the application can interact to databases, web services, or other things. They handle the technical details.

Driver Actors

Driver actors are the initiators of communication with the core. They reach out to the core to request specific services. Examples of driver actors can be HTTP request or command line interfaces (CLI).

Driven Actors

Driven actors are the ones that triggered by the core. If the core needs something from external services, it sends a request to the adapter, instructing it to perform a particular action. For example, if the core needs to store data in a Postgres database, it triggers communication with the Postgres client to execute an INSERT query. In this scenario, the core initiates the communication.

Implementation

Requirements

Asdita had been kind enough to provide the database schema design and Postman API documentation. So all I had to do was just structure my application to adhere to hexagonal architecture principles and start coding.

Tech Stacks

For building the RESTful Point of Sale service API, I've considered and selected a combination of technologies that would work seamlessly together. For handling HTTP requests and responses, using the Gin HTTP web framework would make sense because I think it seems complete and popular among Go community too. To ensure data integrity and persistence, I'm using PostgreSQL database with pgx as the database driver, the reason I choose PostgreSQL because it is the most popular relational database to use in production and offers efficient Go integration. I'm also implementing caching using Redis with go-redis client library, which provides powerful in-memory data storage capabilities.

Database Schema

Go-POS Schema

  • users: stores user information, including names, emails, passwords, roles (admin or cashier), and timestamps for creation and updates.
  • payments: stores various payment methods. It tracks payment types, names, logos, and timestamps for creation and updates.
  • categories: stores product categories, tracking category names and timestamps for creation and updates.
  • products: stores essential product data, including category IDs, SKUs, names, stock levels, prices, images, and timestamps for creation and updates.
  • orders: stores order details, such as user IDs, payment IDs, customer names, total prices, payments made, returns, receipt codes, and timestamps for creation and updates.
  • order_products: stores record of products included in each order. It notes order and product IDs, quantities, total prices, and timestamps for creation and updates.

Application Architecture

Go-POS Hexagonal Architecture

The application follows Hexagonal Architecture, depicted as a hexagon. The core at its center houses domain and service containing business logic.

On the left side of core is a driver port, an entry point for driver adapter, while on the right side there are 2 driven ports that act as the gate for the core to interact with driven adapter.

Around the core, adapters bridge the core and external world. On the left, the HTTP adapter handles incoming HTTP requests. On the right, 2 adapters exist: DB adapter connects to PostgreSQL and Cache adapter works with Redis.

The driver actors and driven actors resides outside of the application. Nginx, on the left, serves as an HTTP server, the driver actor. On the right, PostgreSQL and Redis represent driven actors responding to core requests.

Project Structure



.
├── bin
├── cmd
│   └── http
├── docs
└── internal
    ├── adapter
    │   ├── cache
    │   │   └── redis
    │   ├── handler
    │   │   └── http
    │   ├── repository
    │   │   └── postgres
    │   │       └── migrations
    │   └── token
    │       └── paseto
    └── core
        ├── domain
        ├── port
        ├── service
        └── util

21 directories



Enter fullscreen mode Exit fullscreen mode
  • bin: directory to store compiled executable binary.
  • docs: directory to store project's documentation, such as swagger static files.
  • cmd: directory for main entry points or commands of the application. The http sub-directory holds the main HTTP server entry point.
  • internal: directory for containing application code that should not exposed to external packages.
  • core: directory that contains the central business logic of the application. Inside it there are 4 sub-directories.
    • domain: directory that contains domain models/entities representing core business concepts.
    • port: directory that contains defined interfaces or contracts that adapters must follow.
    • service: directory that contains the business logic or services of the application.
    • util: directory that contains utility functions that reused in the service package.
  • adapters: directory for containing external services that will interact with the core of application. There are 4 external services used in this application.
    • handler/http: directory that contains HTTP request and response handler.
    • repository/postgres: directory that contains database adapters for PostgreSQL.
    • cache/redis: directory that contains cache adapters for Redis.
    • token/paseto: directory that contains token generation and validation adapters using Paseto.

Dev tools

I've used several tools to ease things out during development. These tools help in different ways.

Podman

Podman is a containerization tool similar to Docker, it manages containers for the application. It simplifies tasks such as building, running, and maintaining containers. It is ideal for creating isolated development environments such as PostgreSQL and Redis instance.

DBDocs

DBDocs serves as a database documentation generator. It automates the process of generating documentation for the database schema, aiding in comprehending and managing the database structure, especially when working with relational databases like PostgreSQL.

Taskfile

Taskfile is a tool for streamlining repetitive development tasks. It helps automate activities like building, testing, and deploying applications. Unlike Makefile, Taskfile uses YAML for configuration, making it more readable and user-friendly.

Golang-migrate

Golang-migrate is a database migration tool designed for Go applications. It helps manage and apply changes to the database schema as the application grows, ensuring that the code and database structure stay in sync.

Air

Air is an automatic reloading tool for Go applications. It keeps an eye on code changes and automatically restarts the application, making development more efficient.

Swaggo

Swaggo is a tool that creates Swagger documentation for Go APIs. It makes documenting API endpoints easier, helping developers understand and use the API.

Golangci-lint

Golangci-lint is a tool for checking Go code quality, finding issues, bugs, and style problems. It helps keep the code clean and maintainable.

Github Actions

GitHub Actions is a platform linked with GitHub repositories for automating tasks like building, testing, and deploying applications. It simplifies development and ensures code quality.

Deployment

I have created a GitHub Action workflow to automate the containerization process of the application and put it on GitHub Container Registry. Then I manually deployed it on my personal VPS with Nginx as reverse proxy. I might consider writing another blog post to explain my VPS setup.

You can explore the application's Swagger documentation at this URL: https://gopos.bagashiz.me/docs/index.html

Go-POS Swagger Live Documentation

Source Code

I don't want to make this post too long. So if you are interested, please check out the complete source code for this project on my GitHub repository. You can access and explore the code base, review the application's structure, and even contribute to its development.

GitHub logo bagashiz / go-pos

Simple RESTful Point of Sale (POS) Service API written in Go using Gin web framework, PostgreSQL database, and Redis cache. Proof of concept of implementing Hexagonal Architecture in Go.

Go POS

Description

A simple RESTful Point of Sale (POS) web service written in Go programming language. This project is a part of my learning process in understanding Hexagonal Architecture in Go.

It uses Gin as the HTTP framework and PostgreSQL as the database with pgx as the driver and Squirrel as the query builder. It also utilizes Redis as the caching layer with go-redis as the client.

This project idea was inspired by the Ide Project untuk Upgrade Portfolio Backend Engineer video on YouTube by Asdita Prasetya, which provided valuable guidance and inspiration for its development.

Getting Started

  1. If you do not use devcontainer, ensure you have Go 1.23 or higher and Task installed on your machine:

    go version && task --version
    Enter fullscreen mode Exit fullscreen mode
  2. Create a copy of the .env.example file and rename it to .env:

    cp .env.example .env
    Enter fullscreen mode Exit fullscreen mode

    Update configuration values as needed.

  3. Install all dependencies, run docker…





Improvements

I admit that there are a few areas of this project that still have room for improvement:

  • As of now, unit tests haven't been implemented yet.
  • The project currently uses slog package from standard library for logging. But switching to a more advanced logger like zap could offer more flexibility and features.
  • Instead of directly accessing environment variables with os.Getenv(), integrating a configuration handler like viper might make it maintainable.

References

Top comments (7)

Collapse
 
magicwarms profile image
Andhana Utama

Awesome brother! me as a Backend engineer from Indonesia too! Hello!

Collapse
 
bagashiz profile image
Bagas Hizbullah

Halo, Salam kenal yaa!

Collapse
 
azhe403 profile image
Azhe Kun

Thanks for this great article ngab!

Collapse
 
dumebii profile image
Dumebi Okolo

Awesome!

Collapse
 
arthasmx profile image
Arthas MX

This is cool, i'm learning go...

Gracias!

Collapse
 
batsys profile image
Bat-Erdene Tsedendamba

Thanks, like on github

Collapse
 
aman45 profile image
Aman • Edited

Thanks for sharing this quality information with us. I really enjoyed reading. onstream.so Telugu