Understanding Interfaces

Well first of all an interface is an abstractor of behaviour. The interface is a tool so that you can wire dependencies just by their behaviour, not by how they implement that behaviour. Let’s look at php for example:

interface UserRepositoryInterface {

public function save(User $user); public function get(string $id): User; public function list(): Users;

}

Then we will build our UserRepository:

class UserRepository implements UserRepositoryInterface {

public function save(User $user)

{

// do something to save

}



public function get(string $id): User

{

// do something to get the User by id

} public function list(): Users

{

// do something to get the list of Users

}

}

Every method present in the interface must be implemented by the class. The class must respect entirely the interface like it was a rigid contract. This is an example of an explicit interface, where in the class is stated what interface to implement.

Now, let’s see an example in golang:

type UserSaver interface {

Save(user entity.User) error

}

The next struct will implicitly implement this interface:

type UserRepository struct {

databaseClient someClient

} func (u UserRepository) Save (user entity.User) error {

databaseClient.insert(user)

} func (u UserRepository) GetByEmail(email string) (entity.User, error) {

databaseClient.getByEmail(email)

}

Notice how we didn’t specify anything in the UserRepository about the UserSaver?

This is an implicit interface, the only thing that matters is that the UserRepository has a Save method with the signature in UserSaver interface.

This was one of the hardest things to interiorize when I was learning go but in the end was probably the major factor to fall in love with this language.

It becomes easier to abstract dependencies, because if I am implementing a new module I can define an interface with the signature I want without importing another module.

Composing Interfaces

Well as you saw previously the PHP Interface has two more methods and the Go Interface only one.

One of the ideias of Go interfaces is to keep it simple and short.

If we only need the save method we can specify an interface on the fly containing only the save method.

Imagine that we want to implement a User Register use case. First we create the save method:

type UserSaver interface {

Save(user entity.User) error

} func SaveIfValid(userSaver UserSaver, user entity.User) error {

err := user.Validate();

if err != nil {

return err

}



userSaver.Save(user)



return nil

}

Then we implement the method that check if the user already exists:

type UserByEmailGetter interface {

GetByEmail(email string) error

} func userExists(userByEmailGetter UserByEmailGetter, email string)

(bool, error) {

user, err := userByEmailGetter.GetByEmail(email)

if err != nil {

return false, err

}



if user.id == nil {

return false, nil

} return true, nil

}

In this methods we need two different interfaces that are present in the userRepository and we need somehow wire that repository in this module. We need to combine both interfaces:

type UserEmailGetterAndSaver interface{

UserByEmailGetter

UserSaver

}

Finally we can instantiate our class and wire our repository:

type UserRegisterUseCase struct {

repo userEmailGetterAndSaver

} func newUserRegisterUseCase(repo userEmailGetterAndSaver) UserRegisterUseCase {

return &UserRegisterUseCase{repo}

}

Hope you enjoyed it

Mick Bolt