Composing Interfaces in Go

Rafael Jesus
3 min readMar 5, 2018

Composing interfaces in Go is one of the features I like the most in the language. In this article we are going to write a real use case composing small interfaces.

To demonstrate this concept, let’s imagine a hypothetical scenario where we have two structs which manage users and execute http calls:

Sync and Store are structs responsible for managing users in our system, given both executes http requests they expect to receive as a dependency a structure which satisfies the HTTPClient interface.

Let’s see the HTTPClient definition:

Nice, we have one struct for managing users sync, and other for managing store logic. It sounds to be very easy to test, given they do one thing and do it well, also both accepts an interface as argument. The way we designed our code, all we need to do is to create a HTTPClientMock and that’s it.

Let’s see how the implementation of Sync unit test would look like:

Our test works pretty well, but note that Sync doesn’t use the Get method from HTTPClient

Clients should not be forced to depend on methods they do not use. –Robert C. Martin

Another downside is we might have to implement every new method, which might be added in the HTTPClient e.g Patch, Delete, Put to our mock struct, making much harder to test and increasing the complexity while reading the code. If you change the Get method signature will affect Sync tests which doesn’t use this method. It’s a unnecessary coupling and we should get rid of.

In this hypothetical example we are mocking theHTTPClient with two methods only, but imagine a scenario where you need to expose a new handler for listening to the messaging system, and this handler responsibility is to receive a payload and store it on the database:

The AMQPHandler only cares about the Add method which manages atomic operation for storing the user, and as you might have guessed, the Repository mock would have the following anomaly:

Due to our poor design we have no option, every client of Repository must implement the methods defined by the contract.

This is the opposite of what interfaces in Go tend to be, in Go interfaces should be small, limited to one or two methods, and this case Repository absolutely exceeds it, making extremely hard to re-use due it’s huge contract.

Composing

The bigger the interface, the weaker the abstraction, Rob Pike

Back to our program for managing users, the Sync struct should be expecting to receive an interface which satisfies the Post method. The one that needs both Post and Get is Store

So let’s re-design Sync a little bit:

The code speaks by itself, it’s much clear what is being passed to NewSync constructor, much easier to test and we don’t need to manage the whole contract of HTTPClient interface.

Let’s see how Store is managing to receive HTTPClient as a dependency:

To be honest there are no news so far, the way HTTPClient was designed is pretty much the same as ReadWriter:

type ReadWriter interface {
Reader
Writer
}

Leveraging interface composition allowed us to make the dependencies clear.

The astute readers might have noticed some TDD in action here, without unit tests I would say one is unable to achieve such design in the first attempt. Also note no external libraries were required for asserting, mock and so on — all credits to

You might be wondering how would be the implementation of HTTPClient?

As simple as that, HTTPClient just need to implement Post and Get. Note we are not returning any interface like:

type HTTPClient interface {
Post(...)
Get(...)
}func New(...) HTTPClient {
// returns a public/private struct
}

The implementing package should return concrete (usually pointer or struct) types, see CodeReview in the Golang repository.

Let the consumer package declare it, in this case user pkg:

Finally we tie all dependencies in our main.go pkg creating and passing them downstream to Sync, Store and so on:

Conclusion

From now on with this real world example, I hope you start using Interface Segregation Principle in order to write more idiomatic Go code with clear dependencies, easier to test and to reason about.

In the next post, we will make HTTPClient resilient to failures with circuit breakers and retry logic, stay tuned.

Show me the code

You can check the source code containing the full implementation

Acknowledgements

Thanks to my friends Bastian and Felipe for reviewing this article.

--

--