I'm trying to figure out the idiomatic way to structure models in Go but I'm having trouble finding any samples for larger enterprise type apps (just lots of Cats and Dogs that speak...).
I started off by putting each of my models into a separate package since that seemed to produce the cleanest API to use the models:
import "models/person"
person.New(...) // returns the newly created person
person.GetById(123) // returns a single person
person.GetAll() // returns a list of people
However, then I ran into the problem that my models need to reference each other quite a bit. I ended up with packages that look like the following:
-- File person.go
package Person
import "models/team"
type Person struct {
Name string
Team Team
}
func (p *Person) New(...) *Person {
...
}
-- File team.go
package Team
import "models/person"
type Team struct {
Name string
People []*Person
}
func (t *Team) New(...) *Team {
...
}
This doesn't work because now I have a cyclic reference. Should I just be adding all of these models to the same package so that the API looks like this?
import "model"
model.NewPerson(...) // returns the newly created person
model.GetPersonById(123) // returns a single person
model.GetAllPeople() // returns a list of people
Or should I be using interfaces to solve this (and if so, what would they look like)?
I also have questions on how to handle things like database connections. How do people normally provide a database connection to their models (either directly or through some intermediate object)? Does every call need to take an interface to some database as a parameter or is there a better way to do it?
Is there a larger example of structuring a full Rest API in Go somewhere? I found one example here but it's still pretty small and the author notes that he is a beginner at Go so I'm not sure how much of that to trust.
Thanks!
There is no need to put each model in a separate package. In fact, they could probably just go in package main. If several packages have all sorts of mutual dependencies on each other, they aren't conceptually separate enough to be separate packages anyway.
No need to merge all models into a single package only to break the cyclic import problem. I suggest to go with interfaces instead.
Some (probably leaf) package defines only the interface that defines your model behavior. Note: interfaces do not define data. But any data can be abstracted as behavior via getter and setter methods. Then you can have any number of entities which satisfy that interface in any place you prefer - in separate packages or not.
This approach also makes testing a (possibly mocked) model easier.