Import cycles in Golang

Cyclic dependency is fundamentally a bad design and is a compile-time error in Golang.
So what is a cyclic dependency?
Let’s say we have a struct A and struct B in package ‘a’ and ‘b’ respectively. struct A is dependent on struct B and struct B is dependent on struct A, then we can say there is a cyclic dependency or an import cycle in Golang.
Cyclic dependencies can be straight forward as described above or sometimes they are in layers.
Let’s look at some example
package aimport (
"fmt""isha/b"
)type A struct {
}func (a A) DoSomethingWithA() {
fmt.Println(a)
}func CreateA() *A {
a := A{}
return a
}func invokeSomethingFromB() {
o := b.CreateB()
o.doSomethingWithB()
}
Package b
package bimport (
"fmt""isha/a"
)type B struct {
}func (b B) doSomethingWithB() {
fmt.Println(b)
}func CreateB() *B {
b := B{}
return b
}func invokeSomethingFromA() {
o := a.CreateA()
o.DoSomethingWithA()
}
Now if you see A needs B and B needs A and hence we have got an import cycle here or a cyclic dependency. When you compile this code, it will return this error
import cycle not allowedWhat can we do about it?
To avoid the cyclic dependency, we must introduce an interface in a new package say x. This interface will have all the methods that are in struct A and are accessed by struct B.
package xtype ADoSomethingIntf interface {
DoSomethingWithA()
}
Now we will modify our code in package b to import x instead of a
package bimport (
"fmt""isha/x"
)type B struct {
}func (b B) doSomethingWithB() {
fmt.Println(b)
}func NewB() *B {
b := B{}
return b
}func invokeSomethingFromA(o x.ADoSomethingIntf) {
o.DoSomethingWithA()
}
But since we still need the instance of A, we will have to introduce one more package lets say y, Package y will import both a and b, it creates an instance of a and passes it to b.
package yimport (
"isha/a"
"isha/b"
)func doSomethingWithY() {
o := a.NewA()
b.invokeSomethingFromA(o)
}
This is how we have broken the cycle, now
A depends on B
B depends on X
Y depends on A and B
hence no cycle :-)
