Skip to main content

Package loading

Package loading groups multiple service registrations.

Registration

The services can be assembled into a package, and then, exported all at once.

package stores

var Package = do.Package(
do.Lazy(NewPostgreSQLConnectionService),
do.Lazy(NewUserRepository),
do.Lazy(NewArticleRepository),
)

Play: https://go.dev/play/p/kmf8aOVyj96

The traditional vocab can be translated for package registration:

  • Provide[T](Injector, Provider[T]) -> Lazy(Provider[T])
  • ProvideNamed[T](Injector, string, Provider[T]) -> LazyNamed(string, Provider[T])
  • ProvideValue(Injector, T) -> Eager(T)
  • ProvideNamedValue[T](Injector, string, T) -> EagerNamed(string, T)
  • ProvideTransient[T](Injector, Provider[T]) -> Transient(Provider[T])
  • ProvideNamedTransient[T](Injector, string, Provider[T]) -> TransientNamed(string, Provider[T])
  • As[Initial, Alias](Injector) -> Bind[Initial, Alias]()
  • AsNamed[Initial, Alias](Injector, string, string) -> BindNamed[Initial, Alias](string, string)

Testing and mocking

A package can ship a second variant, exposing test doubles behind the same interfaces as the production services. Swapping Package for its mock counterpart in a test injector replaces every service it registers, without touching the code under test.

This requires each service to be exposed through an interface (see Accept interfaces, return structs), with both the real and the mock implementation satisfying it.

package repositories

type UserRepository interface {
FindByID(id string) (*User, error)
}

func newUserRepository(i do.Injector) (UserRepository, error) {
pool := do.MustInvoke[*pgxpool.Pool](i)
return &userRepository{pool: pool}, nil
}

type userRepository struct {
pool *pgxpool.Pool
}

func (r *userRepository) FindByID(id string) (*User, error) {
// query the database
}

var _ UserRepository = (*userRepository)(nil)

Play: https://go.dev/play/p/s-ZWLUGiMaT

tip

Only mock the packages relevant to the test. Combining repositories.Package for services you don't need to fake with repositories.PackageMock for the one you do is a common pattern, as long as both variants don't register the same service twice in the same injector.