Skip to content

feat: add code example for clean architecture page - Farhan/Issue_#236 #23

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions chapter-D.4-clean-architecture-golang/config/.env
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
DATABASE_HOST=127.0.0.1
DATABASE_PORT=5432
DATABASE_USERNAME=postgres
DATABASE_PASSWORD=root
DATABASE_NAME=testing

APP_NAME=clean-architeture-example
APP_HOST=127.0.0.1
APP_PORT=9090
APP_VERSIONAPI=v1
40 changes: 40 additions & 0 deletions chapter-D.4-clean-architecture-golang/config/database.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package config

import (
"time"

"gorm.io/driver/postgres"
"gorm.io/gorm"
)

type DatabaseConfig struct {
Host string
Port string
Username string
Password string
DBname string
}

func NewDatabaseConfig(config DatabaseConfig) (db *gorm.DB, err error) {
stringConnection := "host=" + config.Host + " user=" + config.Username + " password=" + config.Password + " dbname=" + config.DBname + " port=" + config.Port + " TimeZone=UTC"
db, err = gorm.Open(postgres.Open(stringConnection), &gorm.Config{})
if err != nil {
return
}

sqlDB, err := db.DB()
if err != nil {
return
}

// SetConnMaxIdleTime: berfungsi untuk menetapkan jumlah maksimum waktu koneksi secara idle(tidak berjalan)
sqlDB.SetConnMaxIdleTime(1 * time.Minute)
// SetConnMaxLifetime: berfungsi menentukan maksimum waktu dapat digunakan kembali
sqlDB.SetConnMaxLifetime(10 * time.Minute)
// SetMaxIdleConns: berfungsi untuk menentukan jumlah connection tidak dijalankan(idle)
sqlDB.SetMaxIdleConns(20)
// SetMaxOpenConns: berfungsi untuk menetapkan jumlah open koneksi
sqlDB.SetMaxOpenConns(5)

return
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
package controllers

import (
"clean-architecture-golang-example/entities"
"encoding/json"
"log"
"net/http"
"strconv"
)

type ProductController struct {
versionApi string
logger *log.Logger
http *http.ServeMux
usecase *entities.ProductUsecase
}

func NewProductController(versionApi string, logger *log.Logger, http *http.ServeMux, usecase *entities.ProductUsecase) *ProductController {
controller := &ProductController{versionApi, logger, http, usecase}
controller.Route()

return controller
}

func (Controller *ProductController) Gets(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")

if r.Method == http.MethodGet {
id, _ := strconv.Atoi(r.URL.Query().Get("id"))
controller := *Controller.usecase

if id == 0 {
result, err := controller.Gets()
for _, each := range result.Data {
Controller.logger.Println("[SUCCESS] ID : ", each.ID)
}
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
Controller.logger.Println("[ERROR]:" + err.Error())
} else {
w.WriteHeader(http.StatusOK)
Controller.logger.Println("[SUCCESS]: Gets product is success")
}

json.NewEncoder(w).Encode(result)
} else {
result, err := controller.GetOne(id)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
Controller.logger.Println("[ERROR]:" + err.Error())
} else {
w.WriteHeader(http.StatusOK)
Controller.logger.Println("[SUCCESS]: Get product by id is success")
}

json.NewEncoder(w).Encode(result)
}
} else {
w.WriteHeader(http.StatusMethodNotAllowed)
w.Write([]byte("Method not allowed"))
Controller.logger.Println("[ERROR]: method not allowed")
}
}

func (Controller *ProductController) Create(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")

if r.Method == http.MethodPost {
var data entities.CreateProductRequest

controller := *Controller.usecase
dec := json.NewDecoder(r.Body)
dec.DisallowUnknownFields()

err := dec.Decode(&data)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
Controller.logger.Println("[ERROR]:" + err.Error())
}

result, err := controller.Create(data)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
Controller.logger.Println("[ERROR]:" + err.Error())
} else {
w.WriteHeader(http.StatusCreated)
Controller.logger.Println("[SUCCESS]: Create product is success")
}
json.NewEncoder(w).Encode(result)
} else {
w.WriteHeader(http.StatusMethodNotAllowed)
w.Write([]byte("Method not allowed"))
Controller.logger.Println("[ERROR]: method not allowed")
}
}

func (Controller *ProductController) Update(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")

if r.Method == http.MethodPut {
var data entities.UpdateProductRequest

controller := *Controller.usecase
dec := json.NewDecoder(r.Body)
dec.DisallowUnknownFields()

err := dec.Decode(&data)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
Controller.logger.Println("[ERROR]:" + err.Error())
}

result, err := controller.Update(data)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
Controller.logger.Println("[ERROR]:" + err.Error())
} else {
w.WriteHeader(http.StatusOK)
Controller.logger.Println("[SUCCESS] Update product is success")

}
json.NewEncoder(w).Encode(result)
} else {
w.WriteHeader(http.StatusMethodNotAllowed)
w.Write([]byte("Method not allowed"))
Controller.logger.Println("[ERROR]: method not allowed")
}
}

func (Controller *ProductController) Delete(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")

if r.Method == http.MethodDelete {
var data entities.DeleteProductRequest

controller := *Controller.usecase
dec := json.NewDecoder(r.Body)
dec.DisallowUnknownFields()

err := dec.Decode(&data)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
Controller.logger.Println("[ERROR]:" + err.Error())
}

result, err := controller.DeleteByID(data.ID)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
Controller.logger.Println("[ERROR]:" + err.Error())
} else {
w.WriteHeader(http.StatusOK)
Controller.logger.Println("[SUCCESS] Delete product is success")

}
json.NewEncoder(w).Encode(result)
} else {
w.WriteHeader(http.StatusMethodNotAllowed)
w.Write([]byte("Method not allowed"))
Controller.logger.Println("[ERROR]: method not allowed")
}
}

func (Controller *ProductController) Route() {
Controller.http.HandleFunc(Controller.versionApi+"/product/gets", Controller.Gets)
Controller.http.HandleFunc(Controller.versionApi+"/product/create", Controller.Create)
Controller.http.HandleFunc(Controller.versionApi+"/product/update", Controller.Update)
Controller.http.HandleFunc(Controller.versionApi+"/product/delete", Controller.Delete)
}
18 changes: 18 additions & 0 deletions chapter-D.4-clean-architecture-golang/entities/product_request.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package entities

type CreateProductRequest struct {
Name string `json:"name" validate:"required"`
Price float64 `json:"price" validate:"required"`
Weight float64 `json:"weight" validate:"required"`
}

type UpdateProductRequest struct {
ID int `json:"id" validate:"required"`
Name string `json:"name" validate:"required"`
Price float64 `json:"price" validate:"required"`
Weight float64 `json:"weight" validate:"required"`
}

type DeleteProductRequest struct {
ID int `json:"id" validate:"required"`
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package entities

// Struct request and response product
type ProductResponseJSON struct {
Data []Products `json:"data"`
Count int64 `json:"count"`
Success bool `json:"success"`
Message string `json:"message"`
}
30 changes: 30 additions & 0 deletions chapter-D.4-clean-architecture-golang/entities/products.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package entities

import "gorm.io/gorm"

type Products struct {
gorm.Model
ID int `gorm:"column:id"`
Name string `gorm:"column:name"`
Price float64 `gorm:"column:price"`
Weight float64 `gorm:"column:weight"`
}

// Membuat model interface untuk repository product
type ProductRepository interface {
GetByID(id int) (Products, error)
Gets() ([]Products, error)
Create(product *Products) error
Update(product *Products) error
DeleteByID(id int) (Products, error)
Count() (int64, error)
}

// Membuat model interface untuk usecase product
type ProductUsecase interface {
GetOne(id int) (ProductResponseJSON, error)
Gets() (ProductResponseJSON, error)
Create(product CreateProductRequest) (ProductResponseJSON, error)
Update(product UpdateProductRequest) (ProductResponseJSON, error)
DeleteByID(id int) (ProductResponseJSON, error)
}
25 changes: 25 additions & 0 deletions chapter-D.4-clean-architecture-golang/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
module clean-architecture-golang-example

go 1.18

require (
github.com/gabriel-vasile/mimetype v1.4.2 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-playground/validator/v10 v10.15.4 // indirect
github.com/go-sql-driver/mysql v1.7.1 // indirect
github.com/jackc/pgpassfile v1.0.0 // indirect
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect
github.com/jackc/pgx/v5 v5.3.1 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.5 // indirect
github.com/joho/godotenv v1.5.1 // indirect
github.com/leodido/go-urn v1.2.4 // indirect
golang.org/x/crypto v0.8.0 // indirect
golang.org/x/net v0.9.0 // indirect
golang.org/x/sys v0.7.0 // indirect
golang.org/x/text v0.9.0 // indirect
gorm.io/driver/mysql v1.5.1 // indirect
gorm.io/driver/postgres v1.5.2 // indirect
gorm.io/gorm v1.25.4 // indirect
)
61 changes: 61 additions & 0 deletions chapter-D.4-clean-architecture-golang/go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU=
github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA=
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
github.com/go-playground/validator/v10 v10.15.4 h1:zMXza4EpOdooxPel5xDqXEdXG5r+WggpvnAKMsalBjs=
github.com/go-playground/validator/v10 v10.15.4/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI=
github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk=
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
github.com/jackc/pgx/v5 v5.3.1 h1:Fcr8QJ1ZeLi5zsPZqQeUZhNhxfkkKBOgJuYkJHoBOtU=
github.com/jackc/pgx/v5 v5.3.1/go.mod h1:t3JDKnCBlYIc0ewLF0Q7B8MXmoIaBOZj/ic7iHozM/8=
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q=
github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A=
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
golang.org/x/crypto v0.8.0 h1:pd9TJtTueMTVQXzk8E2XESSMQDj/U7OUu0PqJqPXQjQ=
golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE=
golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ=
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
golang.org/x/net v0.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM=
golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68=
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gorm.io/driver/mysql v1.5.1 h1:WUEH5VF9obL/lTtzjmML/5e6VfFR/788coz2uaVCAZw=
gorm.io/driver/mysql v1.5.1/go.mod h1:Jo3Xu7mMhCyj8dlrb3WoCaRd1FhsVh+yMXb1jUInf5o=
gorm.io/driver/postgres v1.5.2 h1:ytTDxxEv+MplXOfFe3Lzm7SjG09fcdb3Z/c056DTBx0=
gorm.io/driver/postgres v1.5.2/go.mod h1:fmpX0m2I1PKuR7mKZiEluwrP3hbs+ps7JIGMUBpCgl8=
gorm.io/gorm v1.25.1/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k=
gorm.io/gorm v1.25.4 h1:iyNd8fNAe8W9dvtlgeRI5zSVZPsq3OpcTu37cYcpCmw=
gorm.io/gorm v1.25.4/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k=
Loading