How do you structure your packages with interfaces and implementations?

When you have, for example, a package database which contains a Database interface and then another package inside the database package for each implementation of the Database interface for different sorts of databases, how do you deal with avoiding cyclic imports.

To draw a cleaner picture, here a specific example from my code. This is my Database interface.

type Database interface {
	DatabaseBinding

	Close() error
	Name() string

	Migrate(args ...string) error

	BeginTransaction() (TransactionWrapper, error)
}

As you can see, there is another Interface DatabaseBinding which holds the methods for the specific database queries which is irrelevant in this case. It also contains a method BeginTransaction which returns a TransactionWrapper interface which also sits in the database package.

Now, when I do the implementation in a package called postgres for example, I need to reference the database package in there. This can lead to problems with import cycles if I want to use contents of the postgres as well as from the database package in another package.

image

Some time ago, I went around this by putting all the interfaces in a separate package, but I found that really messy. But I also don’t really want to put my implementation in the same package as the Interfaces because I might want to split my implementation into separate files.

Now my question is: How do you build your structure around that?

Hi, @zekro, I have the same structure for my database interfaces: Usually I have a persist package that defines a repository interface and then subpackage for specific implementations. The way I avoid cycles is to have more nested packages depend on less nested packages. For example, I think it’s reasonable for the net/http package in Go to import the more generic net package. os/exec probaby “knows” about os. So my persist/sqlpersist package imports from persist but persist can’t import from persist/sqlpersist.

Then my main package is where it all comes together. It will know about both persist and persist/sqlpersist and be able to use the SQL implementation(s) of the interfaces defined in persist.

I found an answer on Stack Overflow here that says the same thing and more: go - Cyclic dependencies and interfaces - Stack Overflow

Oh yeah, I didn’t thoguht about that go does the same thing in the standard library as you said. Thank you very much for your response. :smile:

or there are other approaches, like clean architecture, like this that uses CQRS, DDD and much more:

1 Like