8000 integrate wire - dependency injection tool by anjankow · Pull Request #2 · anjankow/go-starter · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

integrate wire - dependency injection tool #2

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 27 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
8629d7a
add wire to dockerfile
anjankow Apr 20, 2023
a4284ae
integrate wire
anjankow Apr 20, 2023
c21a5bb
add wire generation to makefile
anjankow Apr 20, 2023
dcedbcd
adapt tests
anjankow Apr 20, 2023
a94f752
remove unused init function
anjankow Apr 20, 2023
fb82d89
add wire to go.mod
anjankow Apr 20, 2023
b818b9 8000 2
keep NewServer function signature
anjankow Apr 20, 2023
193c5f8
rename Init to New in provider function names
anjankow Apr 26, 2023
04f8585
rename to providers.go
anjankow Apr 26, 2023
a6af304
add wire to tools, remove from Dockerfile
anjankow May 3, 2023
0d61d28
add more Server comments
anjankow May 3, 2023
c7544ea
add tmp draft of wire integration instructions
anjankow May 3, 2023
09e7f65
use wire gen ./... in Makefile
anjankow May 11, 2023
bfebdff
add IsStructInitialized function
anjankow May 11, 2023
96d57a2
use IsStructInitialized in server Ready function
anjankow May 11, 2023
094fe25
add db to persistence package
anjankow May 11, 2023
dab2c41
move mailer provider to its pkg, nest transport cfg inside mailer cfg
anjankow May 12, 2023
af76c2b
update wire generation
anjankow May 12, 2023
eddde84
Merge branch 'master' of ssh://git-svc.allaboutapps.at:2222/aw/go-sta…
anjankow May 12, 2023
33f1e77
update changelog and instructions
anjankow May 12, 2023
60fd96a
migration to docker compose v2
eklatzer May 25, 2023
539de77
revert nesting SMTP config in Mailer config
anjankow Jun 1, 2023
ee9631c
Removed release date
eklatzer Jun 1, 2023
c81895f
Pull request #62: migration to docker compose v2
eklatzer Aug 9, 2023
759dce4
add mailer wire set
anjankow Jul 21, 2023
5b64d8c
Merge branch 'master' of ssh://git-svc.allaboutapps.at:2222/aw/go-sta…
anjankow Dec 15, 2023
564d3ca
move server-initialization-via-wire.md to docs folder
anjankow Dec 15, 2023
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
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@
- Please follow the update process in *[I just want to update / upgrade my project!](https://github.com/allaboutapps/go-starter/wiki/FAQ#i-just-want-to-update--upgrade-my-project)*.

## Unreleased
- Migration to Docker Compose V2 ([Docker Compose Docs](https://docs.docker.com/compose/reference/))
- **BREAKING** Integrated [wire](https://github.com/google/wire) code generation tool. Please refer to [the details on how to apply it to your project](/docs/server-initialization-via-wire.md).
- Used for `Server` initialization via `InitNewServer` and `InitNewServerWithDB` functions
- Replaced `Init*` methods on `Server` struct with corresponding providers defined accoding to the [wire guideline](https://github.com/google/wire/blob/main/docs/guide.md#defining-providers)
- Moved `SMTPMailTransportConfig` from `Server` config to `Mailer` config
- Added `IsStructInitialized` function and used it to check if server is ready

## 2023-05-03
- Switch [from Go 1.19.3 to Go 1.20.3](https://go.dev/doc/devel/release#go1.20) (requires `./docker-helper.sh --rebuild`).
Expand Down
12 changes: 8 additions & 4 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,9 @@ go-build: ##- (opt) Runs go build.
go-lint: ##- (opt) Runs golangci-lint.
golangci-lint run --timeout 5m

go-generate: ##- (opt) Generates the internal/api/handlers/handlers.go binding.
go-generate: ##- (opt) Go code generation: wire, internal/api/handlers/handlers.go binding
gsdev handlers gen
@$(MAKE) wire

check-handlers: ##- (opt) Checks if implemented handlers match their spec (path).
gsdev handlers check
Expand All @@ -71,7 +72,7 @@ check-script-dir: ##- (opt) Ensures all scripts/**/*.go files have the "//go:bui
@echo "make check-script-dir"
@find ./scripts -type f -name '*.go' | xargs -L1 grep -L '//go:build scripts' || (echo "Error: Found unset '//go:build scripts' in ./scripts/**/*.go!" && exit 1)

# https://github.com/gotestyourself/gotestsum#format
# https://github.com/gotestyourself/gotestsum#format
# w/o cache https://github.com/golang/go/issues/24573 - see "go help testflag"
# note that these tests should not run verbose by default (e.g. use your IDE for this)
# TODO: add test shuffling/seeding when landed in go v1.15 (https://github.com/golang/go/issues/28592)
Expand Down Expand Up @@ -105,7 +106,7 @@ go-test-print-slowest: ##- Print slowest running tests (must be done after runni
gotestsum tool slowest --jsonfile /tmp/test.log --threshold 2s

# TODO: switch to "-m direct" after go 1.17 hits: https://github.com/golang/go/issues/40364
get-go-outdated-modules: ##- (opt) Prints outdated (direct) go modules (from go.mod).
get-go-outdated-modules: ##- (opt) Prints outdated (direct) go modules (from go.mod).
@((go list -u -m -f '{{if and .Update (not .Indirect)}}{{.}}{{end}}' all) 2>/dev/null | grep " ") || echo "go modules are up-to-date."

watch-tests: ##- Watches *.go files and runs package tests on modifications.
Expand Down Expand Up @@ -313,7 +314,7 @@ watch-swagger: ##- Watches *.yml|yaml|gotmpl files in /api and runs 'make swagge
### -----------------------

# Got license issues with some dependencies? Provide a custom lichen --config
# see https://github.com/uw-labs/lichen#config
# see https://github.com/uw-labs/lichen#config
get-licenses: ##- Prints licenses of embedded modules in the compiled bin/app.
lichen bin/app

Expand Down Expand Up @@ -396,6 +397,9 @@ force-module-name: ##- Overwrite occurrences of 'allaboutapps.dev/aw/go-starter'
get-go-ldflags: ##- (opt) Prints used -ldflags as evaluated in Makefile used in make go-build
@echo $(LDFLAGS)

wire: ## Wire files generation
wire gen ./...

# https://gist.github.com/prwhite/8168133 - based on comment from @m000
help: ##- Show common make targets.
@echo "usage: make <target>"
Expand Down
24 changes: 4 additions & 20 deletions cmd/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,25 +86,9 @@ func runServer() {
}))
}

s := api.NewServer(config)

ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
if err := s.InitDB(ctx); err != nil {
cancel()
log.Fatal().Err(err).Msg("Failed to initialize database")
}
cancel()

if err := s.InitMailer(); err != nil {
log.Fatal().Err(err).Msg("Failed to initialize mailer")
}

if err := s.InitPush(); err != nil {
log.Fatal().Err(err).Msg("Failed to initialize push service")
}

if err := s.InitI18n(); err != nil {
log.Fatal().Err(err).Msg("Failed to initialize i18n service")
s, err := api.InitNewServer(config)
if err != nil {
log.Fatal().Err(err).Msg("Failed to initialize server")
}

router.Init(s)
Expand All @@ -123,7 +107,7 @@ func runServer() {
signal.Notify(quit, os.Interrupt, syscall.SIGTERM)
<-quit

ctx, cancel = context.WithTimeout(context.Background(), 30*time.Second)
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()

if err := s.Shutdown(ctx); err != nil && !errors.Is(err, http.ErrServerClosed) {
Expand Down
12 changes: 6 additions & 6 deletions docker-helper.sh
< AE96 /tr>
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
#!/bin/bash

if [ "$1" = "--up" ]; then
docker-compose up --no-start
docker-compose start # ensure we are started, handle also allowed to be consumed by vscode
docker-compose exec service bash
docker compose up --no-start
docker compose start # ensure we are started, handle also allowed to be consumed by vscode
docker compose exec service bash
fi

if [ "$1" = "--halt" ]; then
docker-compose stop
docker compose stop
fi

if [ "$1" = "--rebuild" ]; then
docker-compose up -d --force-recreate --no-deps --build service
docker compose up -d --force-recreate --no-deps --build service
fi

if [ "$1" = "--destroy" ]; then
docker-compose down --rmi local -v --remove-orphans
docker compose down --rmi local -v --remove-orphans
fi

[ -n "$1" -a \( "$1" = "--up" -o "$1" = "--halt" -o "$1" = "--rebuild" -o "$1" = "--destroy" \) ] \
Expand Down
109 changes: 109 additions & 0 deletions docs/server-initialization-via-wire.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
server initialization via wire
---

# Background

As our projects have grown, we have gradually added more and more dependent components. Consequently, fully initializing the top-level `Server` struct has become a rather complex task, especially considering the lack of reliable protection against accidental usage of an uninitialized `Server` instance.

To simplify our workflow, we made the decision to integrate the **wire** code generation tool into our project. This tool effectively resolves the dependency graph and ensures that the server components are always initialized in the correct order.

> Wire is a code generation tool that automates connecting components using dependency injection.

https://pkg.go.dev/github.com/google/wire.

To accomplish this, we had to introduce certain changes, which may be potentially breaking. We acknowledge that the downstream projects will require some effort in adapting to these changes. Nevertheless, we strongly believe that the long-term benefits will justify the initial investment.

Please read carefully the following instructions.

# 1. Define providers
**BEFORE**: server components initialized within the methods defined on the `Server` struct.
For example:
```go
func (s *Server) InitPush() error {
```

**AFTER**: providers defined according to the wire guideline: https://github.com/google/wire/blob/main/docs/guide.md#defining-providers.

Providers are just ordinary functions getting the necessary dependencies as input parameters and returning an initialized component.
For example:
```go
func NewPush(cfg config.Server, db *sql.DB) (*push.Service, error) {
```

## REQUIRED ACTION
Convert all `func (s *Server) Init*` methods into providers conforming to the wire guidelines (still keeping the server methods for now).

If for any reason a provider function can't live in it's dedicated package, you can place it in wire_providers.go.

Please refer to internal/api/wire.go to check currently used wire providers. They are listed as params to the `wire.Build()` function.

# 2. Define injectors
Injectors are declared in internal/api/wire.go. They instruct wire which providers should be used to satisfy the dependencies of a top level component.

## REQUIRED ACTION
Add defined in the previous step providers to the `InitNewServer*` functions in internal/api/wire.go. More injectors might be added if needed.

## Some hints:
- Components that should be skipped by wire should be labeled with `wire:"-"` (although this is not recommended).
- The order of `wire.Build()` arguments doesn't matter.
- If any provider is missing, wire generation will fail. Also, if any provider failes to create a dependent component in runtime, `InitNewServer*` returns an error. Therefore we can skip manual checks after a server instance has been created.
- If there are two or more components of the same type, declare a custom type for each of them to let wire identify the right provider to be used: https://github.com/google/wire/blob/main/docs/best-practices.md#distinguishing-types. A newly created type should be returned by a corresponding provider.
- If a `Server`'s member is an interface and the corresponding provider returns a pointer to a struct, use the `Bind` function: https://pkg.go.dev/github.com/google/wire#Bind.
- Providers commonly used together might be grouped into sets: https://pkg.go.dev/github.com/google/wire#ProviderSet.

# 3. Generate
Functions declared in wire.go are just recepies for code generation and are excluded from build with the `+build wireinject` directive.
To generate the code, wire command has to be invoked in the directory where wire.go resides.

Generated code can appears in wire_gen.go and looks like:
```go
func InitNewServer(cfg config.Server) (*Server, error) {
db, err := NewDB(cfg)
if err != nil {
return nil, err
}
service, err := NewPush(cfg, db)
```

Because wire.go is excluded from build, function signatures can be duplicated in wire_gen.go. On any wire.go change, wire_gen.go needs to be updated.

Wire generation has been added to Makefile step `go-generate`.

## REQUIRED ACTION
### Wire tool installation:
```sh
make init
```

### Wire generation:
```sh
wire gen ./...
```
or via `make`:
```sh
make go-generate
```

On errors you can simply remove wire_gen file and try again - sometimes it helps.
Otherwire, see [this section](#some-hints) or get familiar with the wire documentation.

# 4. Integrate into existing code

After successful generation, `func (s *Server) Init*` methods are no longer needed. We can remove them and update each place where they have been used previously to use newly generated functions.

## REQUIRED ACTION
Remove all `func (s *Server) Init*` methods and run
```
make go-build
```
The at each place where the compilation failed do the following:
- remove the usage of these methods,
- replace `NewServer(cfg)` or any other previously used `Server` provider with the corresponding function generated by wire, for example `InitNewServer`.

Sometimes having fully initialized server is not required (see scripts/internal/handlers/check.go). In such cases, components we want to use need a manual initialization - the providers have to be invoked in the right order (that could be copied from wire_gen) and the resulting components have to be assigned to the `Server`.

When you're done, verify the sweet fruits of your labor with:
```
make all
make test
```
4 changes: 4 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ require (
github.com/go-openapi/strfmt v0.21.3
github.com/go-openapi/swag v0.22.3
github.com/go-openapi/validate v0.22.0
github.com/google/wire v0.5.0
github.com/jordan-wright/email v4.0.1-0.20210109023952-943e75fe5223+incompatible
github.com/kat-co/vala v0.0.0-20170210184112-42e1d8b61f12
github.com/labstack/echo/v4 v4.9.1
Expand Down Expand Up @@ -68,6 +69,7 @@ require (
github.com/golang-sql/sqlexp v0.1.0 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/google/subcommands v1.0.1 // indirect
github.com/google/uuid v1.3.0 // indirect
github.com/googleapis/enterprise-certificate-proxy v0.2.0 // indirect
github.com/googleapis/gax-go/v2 v2.7.0 // indirect
Expand Down Expand Up @@ -107,9 +109,11 @@ require (
github.com/volatiletech/inflect v0.0.1 // indirect
go.mongodb.org/mongo-driver v1.11.0 // indirect
go.opencensus.io v0.24.0 // indirect
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect
golang.org/x/net v0.7.0 // indirect
golang.org/x/oauth2 v0.2.0 // indirect
golang.org/x/time v0.2.0 // indirect
golang.org/x/tools v0.1.12 // indirect
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/genproto v0.0.0-20221201204527-e3fa12d562f3 // indirect
Expand Down
7 changes: 7 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -383,11 +383,15 @@ github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLe
github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/subcommands v1.0.1 h1:/eqq+otEXm5vhfBrbREPCSVQbvofip6kIz+mX5TUH7k=
github.com/google/subcommands v1.0.1/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk=
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/wire v0.5.0 h1:I7ELFeVBr3yfPIcc8+MWvrjk+3VjbcSzoXm3JVa+jD8=
github.com/google/wire v0.5.0/go.mod h1:ngWDr9Qvq3yZA10YrxfyGELY/AFWGVpy9c1LTRi1EoU=
github.com/googleapis/enterprise-certificate-proxy v0.2.0 h1:y8Yozv7SZtlU//QXbezB6QkpuE6jMD2/gfzk4AftXjs=
github.com/googleapis/enterprise-certificate-proxy v0.2.0/go.mod h1:8C0jb7/mgJe/9KK8Lm7X9ctZC2t60YyIpYEI16jx0Qg=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
Expand Down Expand Up @@ -802,6 +806,7 @@ golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
Expand Down Expand Up @@ -1007,6 +1012,7 @@ golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3
golang.org/x/tools v0.0.0-20190329151228-23e29df326fe/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190416151739-9c9e1878f421/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190420181800-aa740d480789/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190422233926-fe54fb35175b/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
Expand Down Expand Up @@ -1062,6 +1068,7 @@ golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo=
golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
Expand Down
Loading
0