bxcodec / go-clean-arch

Go (Golang) Clean Architecture based on Reading Uncle Bob's Clean Architecture
MIT License
9.03k stars 1.19k forks source link

feat: introduce v4 #88

Closed bxcodec closed 6 months ago

bxcodec commented 6 months ago

Code Directory V4

*ps I'm just updating this to my recent project structure. It may be different from the others, but it should be easy to learn as the first start before learning more techniques and pattern

New Code structure :

.
├── Dockerfile
├── LICENSE
├── Makefile
├── README.md
├── app
│   └── main.go
├── article
│   ├── mocks
│   │   ├── ArticleRepository.go
│   │   └── AuthorRepository.go
│   ├── service.go
│   └── service_test.go
├── article.sql
├── clean-arch.png
├── compose.yaml
├── domain
│   ├── article.go
│   ├── author.go
│   └── errors.go
├── example.env
├── go.mod
├── go.sum
├── internal
│   ├── README.md
│   ├── repository
│   │   ├── helper.go
│   │   └── mysql
│   │       ├── article.go
│   │       ├── article_test.go
│   │       ├── author.go
│   │       └── author_test.go
│   ├── rest
│   │   ├── article.go
│   │   ├── article_test.go
│   │   ├── middleware
│   │   │   ├── cors.go
│   │   │   ├── cors_test.go
│   │   │   └── timeout.go
│   │   └── mocks
│   │       └── ArticleService.go
│   └── workers
│       └── README.md
└── misc
    └── make
        ├── help.Makefile
        └── tools.Makefile

Changelogs

1. Declare Interface to the consuming side

Move all interfaces from domain to the consuming side. E.g., the rest/article.go layer depends on the service so that it will consume the ArticleService interface. Instead of referring to the domain, the interface will be declared in the rest/article.go package. The same goes for repositories interfaces that are declared in article/service.go

// path: internal/rest/article.go

type ArticleService interface {
    Fetch(ctx context.Context, cursor string, num int64) ([]domain.Article, string, error)
    GetByID(ctx context.Context, id int64) (domain.Article, error)
    Update(ctx context.Context, ar *domain.Article) error
    GetByTitle(ctx context.Context, title string) (domain.Article, error)
    Store(context.Context, *domain.Article) error
    Delete(ctx context.Context, id int64) error
}

type ArticleHandler struct {
    Service ArticleService
}

const defaultNum = 10

func NewArticleHandler(e *echo.Echo, svc ArticleService) {
    handler := &ArticleHandler{
        Service: svc,
    }
    e.GET("/articles", handler.FetchArticle)
    e.POST("/articles", handler.Store)
    e.GET("/articles/:id", handler.GetByID)
    e.DELETE("/articles/:id", handler.Delete)
}

2. internal package

The internal package hides the service-specific detail implementation, e.g., database, rest, cache, etc. Moving this implementation to internal will hide the implementations if the project is imported from another project. But it still shows all the core logic (domain and service).

├── internal
│   ├── README.md
│   ├── repository
│   │   ├── helper.go
│   │   └── mysql
│   │       ├── article.go
│   │       ├── article_test.go
│   │       ├── author.go
│   │       └── author_test.go
│   ├── rest
│   │   ├── article.go
│   │   ├── article_test.go
│   │   ├── middleware
│   │   │   ├── cors.go
│   │   │   ├── cors_test.go
│   │   │   └── timeout.go
│   │   └── mocks
│   │       └── ArticleService.go
│   └── workers
│       └── README.md

3. Service-focused package

The article package will only contain all business logic related to the article domain <> in the previous version it contains all (service, repository, controller). And this package now will called as service layer.

Previously

article
├── delivery
│   └── http
│       ├── article_handler.go
│       └── article_test.go
├── mocks
│   ├── ArticleRepository.go
│   └── ArticleUsecase.go
├── repository //Encapsulated Implementation of Repository Interface
│   ├── mysql_article.go
│   └── mysqlarticle_test.go
├── repository.go // Repository Interface
├── usecase //Encapsulated Implementation of Usecase Interface
│   ├── articleucase_test.go
│   └── artilce_ucase.go
└── usecase.go // Usecase Interface.

New Structure

├── article
│   ├── mocks
│   │   ├── ArticleRepository.go
│   │   └── AuthorRepository.go
│   ├── service.go
│   └── service_test.go