Hackernoon logoCreating Clean Architecture Using Golang by@namkount

Creating Clean Architecture Using Golang

Hi guys, I’m a Front-End developer. Before there was a time 2 years working CMS Magento, I like CMS Magento because system clean, and architecture database EAV (Entity — Attribute — Value) but I don’t understand why present I’m dev front-end (I don’t know) in the 2-year current. And present I chance comeback working position Back-End that I like.
I’ve read some articles following:

Summary

If you have read through the article Uncle bob, he said that: > Each has at least one layer for business rules, and another for interfaces.
So, each of these architectures produce systems that are:
  • Independent of Frameworks. The architecture does not depend on the existence of some library of feature laden software. This allows you to use such frameworks as tools, rather than having to cram your system into their limited constraints.
  • Testable. The business rules can be tested without the UI, Database, Web Server, or any other external element.Independent of UI. The UI can change easily, without changing the rest of the system. A Web UI could be replaced with a console UI, for example, without changing the business rules.Independent of Database. You can swap out Oracle or SQL Server, for Mongo, BigTable, CouchDB, or something else. Your business rules are not bound to the database.Independent of any external agency. In fact your business rules simply don’t know anything at all about the outside world.
In architecture of Uncle Bob divide 4 layers:
  • Entites
  • Use Cases
  • Controller
  • Framerwork & Devices
In my project divide layers:
  • Models
  • Use case
  • Repository
  • Delivery
Models:
Models is same as entites. It set of data struct, interface and method, functions. Example model: Book
import (
	"context"
)

// Book ...
type Book struct {
	ID     uint   `json:"id" gorm:"primary_key"`
	Title  string `json:"title"`
	Author string `json:"author"`
}

type BookUsecase interface {
	Fetch(ctx context.Context) ([]Book, error)
	GetByID(ctx context.Context, id string) (Book, error)
}

type BookRepository interface {
	Fetch(ctx context.Context) (res []Book, err error)
	GetByID(ctx context.Context, id string) (Book, error)
}
Usecase:
This a layer will handle problem logic business. This a layer decide repository will use. And have responsibility to provide data to layer delivery use.Layer Usecase a depends layer Repository

import (
	"context"

	"github.com/working/project/domain"
)

type bookUsecase struct {
	bookRepo domain.BookRepository
}

// NewArticleUsecase will create new an articleUsecase object representation of domain.ArticleUsecase interface
func NewBookUsecase(a domain.BookRepository) domain.BookUsecase {
	return &bookUsecase{
		bookRepo: a,
	}
}
func (a *bookUsecase) Fetch(c context.Context) (res []domain.Book, err error) {
	res, err = a.bookRepo.Fetch(c)
	if err != nil {
		return nil, err
	}
	return
}

func (a *bookUsecase) GetByID(c context.Context, id string) (res domain.Book, err error) {
	res, err = a.bookRepo.GetByID(c, id)
	return
}
Repository:
This a layer will handle to database method: fetch, put, delete, get, etc.Layer responsibility will choose DB use in application. Could be Mysql, Postgresql, MongoDB, MariaDB, etc
import (
	"context"

	"github.com/jinzhu/gorm"

	"github.com/working/project/domain"
)

type psqlBookRepository struct {
	Conn *gorm.DB
}

// NewMysqlArticleRepository will create an object that represent the article.Repository interface
func NewPsqlBookRepository(Conn *gorm.DB) domain.BookRepository {
	return &psqlBookRepository{Conn}
}

func (m *psqlBookRepository) Fetch(ctx context.Context) (res []domain.Book, err error) {
	var books []domain.Book
	m.Conn.Find(&books)

	return books, nil
}
func (m *psqlBookRepository) GetByID(ctx context.Context, id string) (res domain.Book, err error) {
	var book domain.Book
	m.Conn.Where("id = ?", id).First(&book)
	return book, nil
}
Deliver:
This a layer will decide how the data present. Could be REST API, HTML, or gRPC whatever the decide type.This a lay accept the input from user and send layer Usecase handle logic

import (
	"github.com/gin-gonic/gin"
	"github.com/working/project/domain"
)

type BookeHandler struct {
	BookUsecase domain.BookUsecase
}

func NewBooksHandler(r *gin.RouterGroup, us domain.BookUsecase) {
	handler := &BookeHandler{
		BookUsecase: us,
	}
	r.GET("/books", handler.FindBooks)
	// r.POST("/books", handler.CreateBook)       // create
	r.GET("/books/:id", handler.FindBook) // find by id
	// r.PATCH("/books/:id", handler.UpdateBook)  // update by id
	// r.DELETE("/books/:id", handler.DeleteBook) // delete by id
}

func (a *BookeHandler) FindBooks(c *gin.Context) {
	books, _ := a.BookUsecase.Fetch(c.Request.Context())
	c.JSON(200, gin.H{"data": books})
}

func (a *BookeHandler) FindBook(c *gin.Context) {
	books, _ := a.BookUsecase.GetByID(c.Request.Context(), c.Param("id"))
	c.JSON(200, gin.H{"data": books})
}
Current my project building on based 4 layers: Model, Usecase, Repository, and Delivery which I present above. And my project interview, I think it not good. I will rebuild the architecture project in article next.
You should understand architecture someone and accept it in a project of you. That will give you look good and remember to start with it simple
Tech stack using:
  • Gorm
  • Gin Framework
  • Postgresql
  • Viper
  • Clean Architecture
If you have a question , or need more explanation, or something, that I can not explain well here, you can ask me from my linkedin or email me. Thank you so much.

Tags

The Noonification banner

Subscribe to get your daily round-up of top tech stories!