(Feat): Initial Commit
This commit is contained in:
106
docs/server-initialization.md
Normal file
106
docs/server-initialization.md
Normal file
@@ -0,0 +1,106 @@
|
||||
# Server Initialization
|
||||
|
||||
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 or attached services.
|
||||
|
||||
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 the following instructions carefully.
|
||||
|
||||
## 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.
|
||||
|
||||
If for any reason a provider function can't live in it's dedicated package, you can place it in `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 the providers defined in the previous step 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.
|
||||
- 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.go` 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
|
||||
```sh
|
||||
make go-build
|
||||
```
|
||||
At each place where the compilation fails 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 a 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 and the resulting components have to be assigned to the `Server`.
|
||||
|
||||
When you're done, verify the sweet fruits of your labor with:
|
||||
```sh
|
||||
make all
|
||||
make test
|
||||
```
|
||||
Reference in New Issue
Block a user