Skip to content

refactor: The main function and some writes what I think is a better practice #68

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 4 commits 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
72 changes: 42 additions & 30 deletions app/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,32 @@ import (
"net/url"
"time"

articleHttpDelivery "github.com/bxcodec/go-clean-arch/article/delivery/http"
httpMiddL "github.com/bxcodec/go-clean-arch/article/delivery/http/middleware"
articleRepo "github.com/bxcodec/go-clean-arch/article/repository/mysql"
articleUcase "github.com/bxcodec/go-clean-arch/article/usecase"
authorRepo "github.com/bxcodec/go-clean-arch/author/repository/mysql"
"github.com/bxcodec/go-clean-arch/domain"

_ "github.com/go-sql-driver/mysql"
"github.com/labstack/echo"
"github.com/spf13/viper"

_articleHttpDelivery "github.com/bxcodec/go-clean-arch/article/delivery/http"
_articleHttpDeliveryMiddleware "github.com/bxcodec/go-clean-arch/article/delivery/http/middleware"
_articleRepo "github.com/bxcodec/go-clean-arch/article/repository/mysql"
_articleUcase "github.com/bxcodec/go-clean-arch/article/usecase"
_authorRepo "github.com/bxcodec/go-clean-arch/author/repository/mysql"
)

func init() {
func main() {
setupConfig()

articleUsecase := initRepositoriesAndUsecase()

e := echo.New()
e.Use(httpMiddL.CORS)

articleHttpDelivery.RegisterHandler(e, articleUsecase)

log.Fatal(e.Start(viper.GetString("server.address")))
}

func setupConfig() {
viper.SetConfigFile(`config.json`)
err := viper.ReadInConfig()
if err != nil {
Expand All @@ -30,7 +44,23 @@ func init() {
}
}

func main() {
func initRepositoriesAndUsecase() domain.ArticleUsecase {
dbConn := initMySQL()
defer func() {
if err := dbConn.Close(); err != nil {
log.Fatal(err)
}
}()
Comment on lines +49 to +53
Copy link
Owner

@bxcodec bxcodec Dec 9, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is not correct, it will close the DB connection immediately when this function is called.

What you can do instead is pass the DBconn as a parameter.
And the DB close function can be called in after the server exit.


authorRepo := authorRepo.NewMysqlAuthorRepository(dbConn)
ar := articleRepo.NewMysqlArticleRepository(dbConn)
timeoutContext := time.Duration(viper.GetInt("context.timeout")) * time.Second
au := articleUcase.NewArticleUsecase(ar, authorRepo, timeoutContext)

return au
}

func initMySQL() *sql.DB {
dbHost := viper.GetString(`database.host`)
dbPort := viper.GetString(`database.port`)
dbUser := viper.GetString(`database.user`)
Expand All @@ -41,32 +71,14 @@ func main() {
val.Add("parseTime", "1")
val.Add("loc", "Asia/Jakarta")
dsn := fmt.Sprintf("%s?%s", connection, val.Encode())
dbConn, err := sql.Open(`mysql`, dsn)

dbConn, err := sql.Open(`mysql`, dsn)
if err != nil {
log.Fatal(err)
}
err = dbConn.Ping()
if err != nil {

if err = dbConn.Ping(); err != nil {
log.Fatal(err)
}

defer func() {
err := dbConn.Close()
if err != nil {
log.Fatal(err)
}
}()

e := echo.New()
middL := _articleHttpDeliveryMiddleware.InitMiddleware()
e.Use(middL.CORS)
authorRepo := _authorRepo.NewMysqlAuthorRepository(dbConn)
ar := _articleRepo.NewMysqlArticleRepository(dbConn)

timeoutContext := time.Duration(viper.GetInt("context.timeout")) * time.Second
au := _articleUcase.NewArticleUsecase(ar, authorRepo, timeoutContext)
_articleHttpDelivery.NewArticleHandler(e, au)

log.Fatal(e.Start(viper.GetString("server.address")))
return dbConn
}
30 changes: 15 additions & 15 deletions article/delivery/http/article_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,25 +4,25 @@ import (
"net/http"
"strconv"

"github.com/bxcodec/go-clean-arch/domain"

"github.com/labstack/echo"
"github.com/sirupsen/logrus"
validator "gopkg.in/go-playground/validator.v9"

"github.com/bxcodec/go-clean-arch/domain"
"gopkg.in/go-playground/validator.v9"
)

// ResponseError represent the reseponse error struct
// ResponseError represent the response error struct
type ResponseError struct {
Message string `json:"message"`
}

// ArticleHandler represent the httphandler for article
// ArticleHandler represent the http handler for article
type ArticleHandler struct {
AUsecase domain.ArticleUsecase
}

// NewArticleHandler will initialize the articles/ resources endpoint
func NewArticleHandler(e *echo.Echo, us domain.ArticleUsecase) {
// RegisterHandler will initialize the articles/ resources endpoint
func RegisterHandler(e *echo.Echo, us domain.ArticleUsecase) {
handler := &ArticleHandler{
AUsecase: us,
}
Expand All @@ -33,13 +33,13 @@ func NewArticleHandler(e *echo.Echo, us domain.ArticleUsecase) {
}

// FetchArticle will fetch the article based on given params
func (a *ArticleHandler) FetchArticle(c echo.Context) error {
func (h *ArticleHandler) FetchArticle(c echo.Context) error {
numS := c.QueryParam("num")
num, _ := strconv.Atoi(numS)
cursor := c.QueryParam("cursor")
ctx := c.Request().Context()

listAr, nextCursor, err := a.AUsecase.Fetch(ctx, cursor, int64(num))
listAr, nextCursor, err := h.AUsecase.Fetch(ctx, cursor, int64(num))
if err != nil {
return c.JSON(getStatusCode(err), ResponseError{Message: err.Error()})
}
Expand All @@ -49,7 +49,7 @@ func (a *ArticleHandler) FetchArticle(c echo.Context) error {
}

// GetByID will get article by given id
func (a *ArticleHandler) GetByID(c echo.Context) error {
func (h *ArticleHandler) GetByID(c echo.Context) error {
idP, err := strconv.Atoi(c.Param("id"))
if err != nil {
return c.JSON(http.StatusNotFound, domain.ErrNotFound.Error())
Expand All @@ -58,7 +58,7 @@ func (a *ArticleHandler) GetByID(c echo.Context) error {
id := int64(idP)
ctx := c.Request().Context()

art, err := a.AUsecase.GetByID(ctx, id)
art, err := h.AUsecase.GetByID(ctx, id)
if err != nil {
return c.JSON(getStatusCode(err), ResponseError{Message: err.Error()})
}
Expand All @@ -76,7 +76,7 @@ func isRequestValid(m *domain.Article) (bool, error) {
}

// Store will store the article by given request body
func (a *ArticleHandler) Store(c echo.Context) (err error) {
func (h *ArticleHandler) Store(c echo.Context) (err error) {
var article domain.Article
err = c.Bind(&article)
if err != nil {
Expand All @@ -89,7 +89,7 @@ func (a *ArticleHandler) Store(c echo.Context) (err error) {
}

ctx := c.Request().Context()
err = a.AUsecase.Store(ctx, &article)
err = h.AUsecase.Store(ctx, &article)
if err != nil {
return c.JSON(getStatusCode(err), ResponseError{Message: err.Error()})
}
Expand All @@ -98,7 +98,7 @@ func (a *ArticleHandler) Store(c echo.Context) (err error) {
}

// Delete will delete article by given param
func (a *ArticleHandler) Delete(c echo.Context) error {
func (h *ArticleHandler) Delete(c echo.Context) error {
idP, err := strconv.Atoi(c.Param("id"))
if err != nil {
return c.JSON(http.StatusNotFound, domain.ErrNotFound.Error())
Expand All @@ -107,7 +107,7 @@ func (a *ArticleHandler) Delete(c echo.Context) error {
id := int64(idP)
ctx := c.Request().Context()

err = a.AUsecase.Delete(ctx, id)
err = h.AUsecase.Delete(ctx, id)
if err != nil {
return c.JSON(getStatusCode(err), ResponseError{Message: err.Error()})
}
Expand Down
12 changes: 1 addition & 11 deletions article/delivery/http/middleware/middleware.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,10 @@ package middleware

import "github.com/labstack/echo"

// GoMiddleware represent the data-struct for middleware
type GoMiddleware struct {
// another stuff , may be needed by middleware
}

// CORS will handle the CORS middleware
func (m *GoMiddleware) CORS(next echo.HandlerFunc) echo.HandlerFunc {
func CORS(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
c.Response().Header().Set("Access-Control-Allow-Origin", "*")
return next(c)
}
}

// InitMiddleware initialize the middleware
func InitMiddleware() *GoMiddleware {
return &GoMiddleware{}
}
3 changes: 3 additions & 0 deletions article/repository/mysql/driver.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package mysql

const DriverMySQL = "mysql"
83 changes: 43 additions & 40 deletions article/repository/mysql/mysql_article.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import (
"github.com/bxcodec/go-clean-arch/domain"
)

var _ domain.ArticleRepository = &mysqlArticleRepository{}

type mysqlArticleRepository struct {
Conn *sql.DB
}
Expand All @@ -20,46 +22,6 @@ func NewMysqlArticleRepository(Conn *sql.DB) domain.ArticleRepository {
return &mysqlArticleRepository{Conn}
}

func (m *mysqlArticleRepository) fetch(ctx context.Context, query string, args ...interface{}) (result []domain.Article, err error) {
rows, err := m.Conn.QueryContext(ctx, query, args...)
if err != nil {
logrus.Error(err)
return nil, err
}

defer func() {
errRow := rows.Close()
if errRow != nil {
logrus.Error(errRow)
}
}()

result = make([]domain.Article, 0)
for rows.Next() {
t := domain.Article{}
authorID := int64(0)
err = rows.Scan(
&t.ID,
&t.Title,
&t.Content,
&authorID,
&t.UpdatedAt,
&t.CreatedAt,
)

if err != nil {
logrus.Error(err)
return nil, err
}
t.Author = domain.Author{
ID: authorID,
}
result = append(result, t)
}

return result, nil
}

func (m *mysqlArticleRepository) Fetch(ctx context.Context, cursor string, num int64) (res []domain.Article, nextCursor string, err error) {
query := `SELECT id,title,content, author_id, updated_at, created_at
FROM article WHERE created_at > ? ORDER BY created_at LIMIT ? `
Expand All @@ -80,6 +42,7 @@ func (m *mysqlArticleRepository) Fetch(ctx context.Context, cursor string, num i

return
}

func (m *mysqlArticleRepository) GetByID(ctx context.Context, id int64) (res domain.Article, err error) {
query := `SELECT id,title,content, author_id, updated_at, created_at
FROM article WHERE ID = ?`
Expand Down Expand Up @@ -182,3 +145,43 @@ func (m *mysqlArticleRepository) Update(ctx context.Context, ar *domain.Article)

return
}

func (m *mysqlArticleRepository) fetch(ctx context.Context, query string, args ...interface{}) (result []domain.Article, err error) {
rows, err := m.Conn.QueryContext(ctx, query, args...)
if err != nil {
logrus.Error(err)
return nil, err
}

defer func() {
errRow := rows.Close()
if errRow != nil {
logrus.Error(errRow)
}
}()

result = make([]domain.Article, 0)
for rows.Next() {
t := domain.Article{}
authorID := int64(0)
err = rows.Scan(
&t.ID,
&t.Title,
&t.Content,
&authorID,
&t.UpdatedAt,
&t.CreatedAt,
)

if err != nil {
logrus.Error(err)
return nil, err
}
t.Author = domain.Author{
ID: authorID,
}
result = append(result, t)
}

return result, nil
}
4 changes: 2 additions & 2 deletions article/usecase/article_ucase.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ import (
"context"
"time"

"github.com/bxcodec/go-clean-arch/domain"

"github.com/sirupsen/logrus"
"golang.org/x/sync/errgroup"

"github.com/bxcodec/go-clean-arch/domain"
)

type articleUsecase struct {
Expand Down
3 changes: 3 additions & 0 deletions author/repository/mysql/driver.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package mysql

const DriverMySQL = "mysql"
Loading