Skip to content

DI and External packages

Levy Araújo Sampaio edited this page Jun 5, 2024 · 3 revisions

General explanation

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.

Creating an new package

real implementation

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
}

Interfaces

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)
}

Injection

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)
}
Clone this wiki locally