diff --git a/README.md b/README.md index e7b4c8d..8e117f9 100644 --- a/README.md +++ b/README.md @@ -58,8 +58,24 @@ $ make test $ make up ``` + +## Pipeline + +* For the pipeline, I typically use Github Actions which is a great tool to run our automatic builds, and could be also used to generate our container images and deployd to our desired platform (k8s, managed containers, functions as service... etc) +* I have used an action to cache the images that we use in tests, (postgres, prometheus, statsd_exporter) so they run faster and our runners do not need to pull images in every single execution. +* Apart from that, it simply relies on makefile to run tests. + ## Author -* **Adolfo Rodriguez** - *go-clean-boilerplate* - [adolsalamanca](https://github.com/adolsalamanca) +* **Adol Rodriguez** - *go-clean-boilerplate* - [adolsalamanca](https://github.com/adolsalamanca) + +## Libraries +* go-metric - Provides a metrics package used to expose application metrics. +* mux - It's basically a Http router commonly used +* pgx - Used as PostgreSQL driver. +* viper - Configuration tool for our applications. +* testify - Testing framework with a lot of capabilities. +* zap - Lightweight structured logger. + ## Articles diff --git a/cmd/go-clean-boilerplate/main.go b/cmd/go-clean-boilerplate/main.go index 0877eaf..31d2ec2 100644 --- a/cmd/go-clean-boilerplate/main.go +++ b/cmd/go-clean-boilerplate/main.go @@ -16,7 +16,7 @@ import ( ) func main() { - logger := log.NewLogger() + logger := log.NewLogger(log.DebugLevel) cfg := config.LoadConfigProvider() err := _interface.Verify(cfg, logger) diff --git a/cmd/go-clean-boilerplate/main_suite_test.go b/cmd/go-clean-boilerplate/main_suite_test.go index 7c70f7b..33e846c 100644 --- a/cmd/go-clean-boilerplate/main_suite_test.go +++ b/cmd/go-clean-boilerplate/main_suite_test.go @@ -60,7 +60,7 @@ func (suite *AcceptanceTestSuite) SetupSuite() { waitForDb() - logger := logger.NewLogger() + logger := logger.NewLogger(logger.DebugLevel) cfg := config.LoadConfigProvider() err := _interface.Verify(cfg, logger) diff --git a/go.mod b/go.mod index 79ccc28..61d2c2b 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/adolsalamanca/go-clean-boilerplate go 1.16 require ( - github.com/armon/go-metrics v0.3.9 // indirect + github.com/armon/go-metrics v0.3.9 github.com/gorilla/mux v1.8.0 github.com/jackc/pgx/v4 v4.13.0 github.com/spf13/viper v1.8.1 diff --git a/go.sum b/go.sum index 4a6eff2..de0d706 100644 --- a/go.sum +++ b/go.sum @@ -183,6 +183,7 @@ github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.1 h1:fv1ep09latC32wFoVwnqcnKJGnMSdBanPczbHAYm1BE= github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= @@ -295,6 +296,7 @@ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lN github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY= github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pelletier/go-toml v1.9.3 h1:zeC5b1GviRUyKYd6OJPvBU/mcVDVoL1OhT17FCt5dSQ= github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= @@ -732,8 +734,8 @@ google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp0 google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s= diff --git a/internal/application/routes.go b/internal/application/routes.go index c447b5d..8ff4dee 100644 --- a/internal/application/routes.go +++ b/internal/application/routes.go @@ -4,7 +4,7 @@ import ( "encoding/json" "net/http" - "github.com/adolsalamanca/go-clean-boilerplate/internal/domain/entities" + "github.com/adolsalamanca/go-clean-boilerplate/internal/domain" "github.com/gorilla/mux" ) @@ -48,7 +48,7 @@ func (s *Server) GetItems() http.HandlerFunc { func (s *Server) CreateItem() http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { - i := entities.Item{} + i := domain.Item{} d := json.NewDecoder(r.Body) err := d.Decode(&i) if err != nil { diff --git a/internal/application/server.go b/internal/application/server.go index c62edcf..d52efe0 100644 --- a/internal/application/server.go +++ b/internal/application/server.go @@ -1,7 +1,7 @@ package application import ( - "github.com/adolsalamanca/go-clean-boilerplate/internal/domain/entities" + "github.com/adolsalamanca/go-clean-boilerplate/internal/domain" _interface "github.com/adolsalamanca/go-clean-boilerplate/internal/interface" "github.com/gorilla/mux" ) @@ -15,8 +15,8 @@ type Server struct { } type Servicer interface { - GetItems() ([]entities.Item, error) - CreateItem(i entities.Item) error + GetItems() ([]domain.Item, error) + CreateItem(i domain.Item) error } func NewServer(service Servicer, logger _interface.Logger, collector _interface.MetricsCollector) *Server { diff --git a/internal/domain/entities/items.go b/internal/domain/items.go similarity index 88% rename from internal/domain/entities/items.go rename to internal/domain/items.go index 7ea50d7..94bffa9 100644 --- a/internal/domain/entities/items.go +++ b/internal/domain/items.go @@ -1,4 +1,4 @@ -package entities +package domain import "time" diff --git a/internal/domain/items_repository.go b/internal/domain/items_repository.go new file mode 100644 index 0000000..8dfa47f --- /dev/null +++ b/internal/domain/items_repository.go @@ -0,0 +1,6 @@ +package domain + +type ItemRepository interface { + FindAllItems() ([]Item, error) + StoreItem(Item) error +} diff --git a/internal/domain/repository/items_repository.go b/internal/domain/repository/items_repository.go deleted file mode 100644 index 51648c3..0000000 --- a/internal/domain/repository/items_repository.go +++ /dev/null @@ -1,8 +0,0 @@ -package repository - -import "github.com/adolsalamanca/go-clean-boilerplate/internal/domain/entities" - -type ItemRepository interface { - FindAllItems() ([]entities.Item, error) - StoreItem(entities.Item) error -} diff --git a/internal/infrastructure/persistence/psql.go b/internal/infrastructure/psql_repository.go similarity index 84% rename from internal/infrastructure/persistence/psql.go rename to internal/infrastructure/psql_repository.go index 2a14266..61b256f 100644 --- a/internal/infrastructure/persistence/psql.go +++ b/internal/infrastructure/psql_repository.go @@ -1,10 +1,10 @@ -package persistence +package infrastructure import ( "context" "fmt" - "github.com/adolsalamanca/go-clean-boilerplate/internal/domain/entities" + "github.com/adolsalamanca/go-clean-boilerplate/internal/domain" "github.com/adolsalamanca/go-clean-boilerplate/pkg/config" "github.com/jackc/pgx/v4/pgxpool" ) @@ -30,7 +30,7 @@ func NewPsqlRepository(config config.Provider) (*PsqlRepository, error) { }, nil } -func (p PsqlRepository) FindAllItems() ([]entities.Item, error) { +func (p PsqlRepository) FindAllItems() ([]domain.Item, error) { conn, err := p.pool.Acquire(context.Background()) if err != nil { return nil, fmt.Errorf("error acquiring connection: %v", err) @@ -38,9 +38,9 @@ func (p PsqlRepository) FindAllItems() ([]entities.Item, error) { defer conn.Release() rows, err := conn.Query(context.Background(), "SELECT id, name, price, created_at, updated_at from ITEMS") - var outputRows []entities.Item + var outputRows []domain.Item for rows.Next() { - row := entities.Item{} + row := domain.Item{} err := rows.Scan(&row.Id, &row.Name, &row.Price, &row.CreatedAt, &row.UpdatedAt) if err != nil { return nil, fmt.Errorf("unexpected error for rows.Values(): %v", err) @@ -51,7 +51,7 @@ func (p PsqlRepository) FindAllItems() ([]entities.Item, error) { return outputRows, nil } -func (p PsqlRepository) StoreItem(i entities.Item) error { +func (p PsqlRepository) StoreItem(i domain.Item) error { conn, err := p.pool.Acquire(context.Background()) if err != nil { return fmt.Errorf("error acquiring connection: %v", err) diff --git a/internal/interface/service.go b/internal/interface/service.go index f54bc8c..b02d504 100644 --- a/internal/interface/service.go +++ b/internal/interface/service.go @@ -1,22 +1,21 @@ package _interface import ( - "github.com/adolsalamanca/go-clean-boilerplate/internal/domain/entities" - "github.com/adolsalamanca/go-clean-boilerplate/internal/domain/repository" - "github.com/adolsalamanca/go-clean-boilerplate/internal/infrastructure/persistence" + "github.com/adolsalamanca/go-clean-boilerplate/internal/domain" + "github.com/adolsalamanca/go-clean-boilerplate/internal/infrastructure" "github.com/adolsalamanca/go-clean-boilerplate/pkg/config" log "github.com/adolsalamanca/go-clean-boilerplate/pkg/logger" ) type Service struct { - repo repository.ItemRepository + repo domain.ItemRepository logger Logger // metrics // tracing } func NewService(config config.Provider, logger Logger, collector MetricsCollector) (*Service, error) { - repo, err := persistence.NewPsqlRepository(config) + repo, err := infrastructure.NewPsqlRepository(config) if err != nil { return nil, err } @@ -26,7 +25,7 @@ func NewService(config config.Provider, logger Logger, collector MetricsCollecto }, nil } -func (s Service) GetItems() ([]entities.Item, error) { +func (s Service) GetItems() ([]domain.Item, error) { i, err := s.repo.FindAllItems() if err != nil { s.logger.Error("could not get items", @@ -39,7 +38,7 @@ func (s Service) GetItems() ([]entities.Item, error) { return i, nil } -func (s Service) CreateItem(i entities.Item) error { +func (s Service) CreateItem(i domain.Item) error { err := s.repo.StoreItem(i) if err != nil { s.logger.Error("could not create items", diff --git a/pkg/logger/logger.go b/pkg/logger/logger.go index c8a4423..bbe93d5 100644 --- a/pkg/logger/logger.go +++ b/pkg/logger/logger.go @@ -7,6 +7,17 @@ import ( "go.uber.org/zap/zapcore" ) +type LogLevel int + +const ( + DebugLevel = LogLevel(zapcore.DebugLevel) + InfoLevel = LogLevel(zapcore.InfoLevel) + WarnLevel = LogLevel(zapcore.WarnLevel) + ErrorLevel = LogLevel(zapcore.ErrorLevel) + PanicLevel = LogLevel(zapcore.PanicLevel) + FatalLevel = LogLevel(zapcore.FatalLevel) +) + type FieldType uint8 type Field struct { @@ -75,7 +86,7 @@ func (s StandardLogger) Error(msg string, fields ...Field) { s.logger.Error(msg, zapFields[:]...) } -func NewLogger() *StandardLogger { +func NewLogger(level LogLevel) *StandardLogger { atom := zap.NewAtomicLevel() encoderCfg := zap.NewProductionEncoderConfig() @@ -89,7 +100,7 @@ func NewLogger() *StandardLogger { )) defer logger.Sync() - atom.SetLevel(zap.DebugLevel) + atom.SetLevel(zapcore.Level(level)) return &StandardLogger{ logger: logger,