I'm relatively new to go and have been doing a massive rewrite trying to reduce my dependency graph as much as possible. I'm pretty happy with where I got it but there is this one part I am not sure how to best handle. If the answer is, "You are going to have that dependency between the two", that's fine too, I'm just looking for a good approach, not expecting miracles.
So below I have two packages, a
& b
, and they both have identical structs. Normally you could convert one to the other in main but each has a subitem which is also a struct and that's stopping Go from allowing it even though the subitems have identical signatures.
One way would be to just reference A.TzConfig in the struct in B and let there be a dependency but that's what I am trying to get rid of.
I guess another way is to create an interface and then get the values of Loc through methods, I think that would work but I haven't tried it yet as that means creating methods for something that's just a structure of data (the actual structure has a lot of items, I reduced it to the essentials for simplicity here) which seems like overkill.
I could move TzConfig into a third module so they would both reference that instead of one referencing the other and that's about all I have thought of.
So my question is, from someone with a real experience, what would be the best way to deal with this scenario in go?
I should mention that the reason they have duplicated structs was just because I was trying to break the dependency between them, the original code just had the struct in one package and the other package referencing it.
package a
type Cfg struct {
Addr string
Loc TzConfig
}
type TzConfig struct {
String string
TZ *time.Location `validate:"noDescent"`
}
func GetCfg() Cfg {
t, _ := time.LoadLocation(`MST`)
return Cfg{
Addr: "abc",
Host: "a.bc.d",
Loc: config.TzConfig{
String: "MST",
TZ: t,
},
}
}
package b
type Cfg struct {
Addr string
Loc TzConfig
}
type TzConfig struct {
String string
TZ *time.Location `validate:"noDescent"`
}
func DoSomethingWithConfig(c Cfg) {
fmt.Println(c)
}
package main
main() {
c := a.GetCfg()
d := b.DoSomethingWithConfig(b.Cg(c))
fmt.Println(d)
}
IMHO, the suggestion provided by @BurakSerdar are completely fine and fits very well for your scenario. I've rewritten the code in this way.
package common
package common
import "time"
type Cfg struct {
Addr string
Loc TzConfig
}
type TzConfig struct {
String string
TZ *time.Location `validate:"noDescent"`
}
Here, you should put the common structs, functions, methods, and so on.
package a
package a
import (
"dependencies/common"
"time"
)
type Cfg struct {
common.Cfg
Host string
}
func GetCfg() Cfg {
t, _ := time.LoadLocation(`MST`)
return Cfg{
Cfg: common.Cfg{
Addr: "abc",
Loc: common.TzConfig{
String: "MST",
TZ: t,
},
},
Host: "a.bc.d",
}
}
Here, you have the specific code related to the package a
that inherits the shared code from the common
package, as you can see in the import
section.
Please note that I used the structs embedding feature to get the shared fields defined within the
common
package.
package b
package b
import (
"dependencies/common"
"fmt"
)
func DoSomethingWithConfig(c common.Cfg) string {
return fmt.Sprint(c)
}
Here, there is nothing special to mention.
package main
package main
import (
"dependencies/a"
"dependencies/b"
"fmt"
)
func main() {
c := a.GetCfg()
d := b.DoSomethingWithConfig(c.Cfg)
fmt.Println(d)
}
Here, the code should be pretty straightforward. I imported packages a
and b
to exploit their functionalities.
Again, I'd like to be clear that this is a subjective topic, hence there isn't a silver bullet solution. To me, looks neat and clear. I would have chosen this way for sure. Let me know and thanks!