-
Notifications
You must be signed in to change notification settings - Fork 5
DI and External packages
Zord is built on hexagonal arch, so the service layer doesn`t know about any external lib, just about business rule. With this principle in mind we have created a Dependency injection sistem based on golang interfaces.
If you want to create a new package is simple, just use the pkg folder and create a new implementation, in this package you need to code the real implementation for any external lib or connection, in the base project we have some essential libs like database and logger.
Example:
type Logger struct {
env string
app string
version string
logger zerolog.Logger
isProduction bool
service string
}
func NewLogger(environment, app, version string) *Logger {
return &Logger{
env: environment,
app: app,
version: version,
isProduction: environment == "production",
}
}
func (l *Logger) Boot() {
zerolog.CallerFieldName = "trace"
zerolog.TimeFieldFormat = zerolog.TimeFormatUnixMicro
l.logger = log.With().
Str("app", l.app).
Str("version", l.version).
Str("environment", l.env).
Logger()
}
func (l *Logger) Debug(Message string, Context ...string) {
l.logger.Debug().Str("service", l.service).Strs("context", Context).Msg(Message)
}
func (l *Logger) Info(Message string, Context ...string) {
l.logger.Info().
Str("service", l.service).Strs("context", Context).Msg(Message)
}
func (l *Logger) Warning(Message string, Context ...string) {
l.logger.Warn().
Str("service", l.service).Strs("context", Context).Msg(Message)
}
func (l *Logger) Error(Error error, Context ...string) {
l.logger.Error().Str("service", l.service).Strs(
"context", Context).
Caller(1).Msg(Error.Error())
}
func (l *Logger) Critical(Error error, Context ...string) {
l.logger.Fatal().Str("service", l.service).Strs(
"context", Context).
Caller(1).Msg(Error.Error())
}
func (l *Logger) SetLogService(service string) {
l.service = service
}
we`ve decided to create external libs interfaces in internal/application/services/base_service.go cause it is called in all services.
Example for above package:
type Logger interface {
Debug(Message string, Context ...string)
Info(Message string, Context ...string)
Warning(Message string, Context ...string)
Error(Error error, Context ...string)
Critical(Error error, Context ...string)
}
We have a DI lib based on golang interface cast, it is called registry and is in pkg/registry folder The registry provider is made on cmd/base_setup.go and there is some examples about how to use it the registry pkg is acessible in all handlers, if you need use the pkg just inject in service constructor (aways type this libs as an interface inside service constructor).
Providing on registry:
l := logger.NewLogger(
conf.ReadConfig("ENVIRONMENT"),
conf.ReadConfig("APP"),
conf.ReadConfig("VERSION"),
)
l.Boot()
Reg.Provide("logger", l)
Service constructor example:
type Service struct {
services.BaseService
logger services.Logger
response *Response
}
//services.Logger is the above interface
func NewService(log services.Logger) *Service {
return &Service{
BaseService: services.BaseService{
Logger: log,
},
}
}
Injection example on handler:
type DummyHandlers struct {
logger *logger.Logger
}
func NewDummyHandlers(reg *registry.Registry) *DummyHandlers {
return &DummyHandlers{
logger: reg.Inject("logger").(*logger.Logger),
}
}
func (hs *DummyHandlers) HandleGetDummy(context echo.Context) error {
s := dummyGet.NewService(hs.logger)
data := new(dummyGet.Data)
if errors := context.Bind(data); errors != nil {
s.CustomError(http.StatusBadRequest, errors)
return context.JSON(s.Error.Status, s.Error)
}
s.Execute(
dummyGet.NewRequest(data),
)
response, err := s.GetResponse()
if err != nil {
return context.JSON(err.Status, err)
}
return context.JSON(http.StatusOK, response)
}