go-clean-echo

Go Echo Simple API with Clean architecture

Stars
63
Committers
1

Go Echo Simple API with Clean Architecture

🤓 About this repo

This is a sample of Web API built by Go (Echo) according to Clean architecture. But I have to say that I'm not a backend specialist nor of Go, so Clean architecture here could be wrong or missing essential concepts. (I'm sorry in that case)

This sample consists of 4 layers, "Models", "Stores", "Services(Logic)" and "Handlers(Framework)", although naming might differ in other samples. Each layer only concerns/handles its inside layer, not the other way around. This makes it super easy to replace each layer. (also good for testing)

What you might find helpful in this repo.

👟 How to run

Install.

git clone [email protected]:zett-8/go-clean-echo.git

cd go-clean-echo

Download dependencies.

go mod download

Fill out auth0 config to run with JWT authentication. Or simply disable JWT middleware

// configs/auth0.go
var Auth0Config = Auth0ConfigType{
    Domain:             "****",
    ClientID:           "****",
    Audience:           []string{"****"},
    Issuer:             "****",
    SignatureAlgorithm: validator.RS256,
    CacheDuration:      15 * time.Minute,
}

// or 

// handlers/handlers.go
func SetApi(e *echo.Echo, h *Handlers, m echo.MiddlewareFunc) {
    g := e.Group("/api/v1")
    g.Use(m) // <- Comment out this line
}

Run docker.

docker-compose up

🌱 Tips

Use multi-stage build

Using multistage build reduces the size of the docker image.

ARG PORT=8888

# Base image for local development. Use 'air' for hot reload.
FROM golang:1.18-alpine as base

ARG PORT
ENV PORT=$PORT
ENV GO_ENV=development

WORKDIR /go/app/base

COPY go.mod .
COPY go.sum .

RUN apk add build-base
RUN go mod download
RUN go install github.com/cosmtrek/air@latest

COPY . .


# The image for build. Set CGO_ENABLE=0 not to build unnecessary binary.
FROM golang:1.18-alpine as builder

ARG PORT
ENV PORT=$PORT

WORKDIR /go/app/builder

COPY --from=base /go/app/base /go/app/builder

RUN CGO_ENABLED=0 go build -o main -ldflags "-s -w"


# The final image to run 
FROM gcr.io/distroless/static-debian11 as production

ARG PORT
ENV PORT=$PORT

WORKDIR /go/app/src

COPY --from=builder /go/app/builder/main /go/app/src/main

EXPOSE $PORT

CMD ["/go/app/src/main"]

Use "Air" for hot reload

We used to have other reloaders such as realize. But it seems no longer be developed or maintained, so I recommend to use "air"

cosmtrek/air.

Mock a unit for testing

Here is one example to mock "service" for "handler" test.

Let's say this is the service to mock.

// services/services.go
type Services struct {
    AuthorService
    BookService
}

func New(s *stores.Stores) *Services {
    return &Services{
        AuthorService: &authorService{s.AuthorStore},
        BookService:   &bookService{s.BookStore},
    }
}

// services/author.go
type (
    AuthorService interface {
        GetAuthors() ([]models.Author, error)
        DeleteAuthor(id int) error
    }

    authorService struct {
        store stores.AuthorStore
    }
)

func (s *AuthorServiceContext) GetAuthors() ([]models.Author, error) {
	r, err := s.store.Get()
	return r, err
}

func (s *AuthorServiceContext) DeleteAuthor(id int) error {
	err := s.store.DeleteById(id)
	return err
}

And in /handlers/author_test.go. Declare Mock struct.

// handlers/author_test.go
type MockAuthorService struct {
	services.AuthorService
	MockGetAuthors       func() ([]models.Author, error)
	MockDeleteAuthorById func(id int) error
}

func (m *MockAuthorService) GetAuthors() ([]models.Author, error) {
	return m.MockGetAuthors()
}

func (m *MockAuthorService) DeleteAuthor(id int) error {
	return m.MockDeleteAuthorById(id)
}

Then use it as mockService.

// handlers/author_test.go
func TestGetAuthorsSuccessCase(t *testing.T) {
    s := &MockAuthorService{
        MockGetAuthors: func () ([]models.Author, error) {
            var r []models.Author
            return r, nil
        },
    }
    
    mockService := &services.Services{AuthorService: s}
    
    e := Echo()
    h := New(mockService)
}

📃 API Document (Swagger)

http://localhost:8888/swagger/index.html

✅ Testing

make test

💜 References

bxcodec/go-clean-arch onakrainikoff/echo-rest-api satishbabariya/go-echo-auth0-middleware xesina/golang-echo-realworld-example-app