I love split my code in a lot of micro-pagakes fully indipendent.

To maximize the isolation each package should be testable and all dependencies injectable and all dependencies should be interfaces.

Let’s analyze this simple package. The function inside the package will use an external library (xyz) to do something.

package mypackage

import 	"github.com/AlessandroLorenzi/postgomock/xyz"

func GetInfoFromXyz() (string, error) {
    xyzSvc := xyz.New(
        os.Getenv("XYZ_KEY_ID"),
        os.Getenv("XYZ_SECRET_KEY"),
    )
    data, err := xyzSvc.GetInfo(42)
    if err != nil {
        return "", err
    }
    return data.InfoINeed, nil
}

This package is quite impossible to test easily.

We have one dependency: xyz. Let’s create an interface that expose what we need:

type Xyz interface{
    GetInfo(int) (*xyz.GetInfoOutput, error)
}

This inteface is mockable, this means that we don’t need to connect to an external service to test this package.

Now let’s create our service.

type Service struct {
    xyzSvc *xyz.XYZ
}

func New(xyzSvc *xyz.XYZ) *Service{
    return &Service
}

func New(xyzSvc Xyz) *Service {
	return &Service{xyzSvc: xyzSvc}
}

Hint: in GoLand you can open Context Action (Alt+Return) ad automatically Generate Constructor.

Now you can modify the GetInfoFromXyz as Service reciver.

func (s *Service) GetInfoFromXyz() (string, error) {
    data, err := s.xyzSvc.GetInfo(42)
    if err != nil {
        return "", err
    }
    return data.InfoINeed, nil
}

In main we can inject xyz connection as dependency. In this example we will share xyz connection between two packages.

package main

func main() {
        os.Getenv("XYZ_KEY_ID"),
        os.Getenv("XYZ_SECRET_KEY"),
    )

    myPackageSvc := mypackage.New(xyzSvc)
    anotherSvc := anotherpackage.New(xyzSvc)

    theValue, _ := myPackageSvc.GetInfoFromXyz()

    anotherSvc.DoStuff(theValue)
}

In part two we will mock xyz and test mypackage