Charles
Charles

Reputation: 1039

How to make a model in Go

I want to make models for my framework, written in go, and I'm not sure how to compose them in a way that shares the common database interaction methods: save, update, delete.

I would normally do this by creating a Model abstract parent class to all concrete models, but Go doesn't have inheritance. You're supposed to use embedding and composition instead, but I don't see how I can embed a model class and have it save the data of the class holding it.

I see the other option, of creating a model class that embeds a concrete model type within it, but I don't really see an interface that would apply to all the models unless it was empty. That brings with it the insecurity that anything can be considered a model.

What do?

Upvotes: 2

Views: 3665

Answers (1)

beatgammit
beatgammit

Reputation: 20205

In my projects I do something like this:

type Storable interface {
    // called after unmarshalling from the database
    Init() error
    // called when an object is being deleted
    // this is useful if the object needs to delete other objects,
    // change state on a remote server, etc.
    Destroy() error
    // called after Init, helps separate initialization from
    // sanity checks (useful to detect errors before using a potentially
    // invalid object)
    Validate() error
    // type of this object, stored in the database in `Save` and `Update`
    // so it can be read out in `Get`
    Type() string
}

If you're working with an SQL database, you could do something like this:

type Schema map[string]reflect.Type

type SQLStorable interface {
    Storable
    Schema() Schema
}

Then in the database, I have functions like this:

func Get(id string) (Storable, error)
func Save(Storable) error
func Update(id string, Storable) error
func Delete(id string) error
// register a type with the database (corresponds to the Type() in Storable)
func Register(typ string, reflect.Type)

I keep a cache of objects in the database: map[string]Storable. This allows me to implement caching logic to reduce lookup times (don't need to reconstruct objects each time it's read from the database).

In my project, I have lots of packages that need to talk with objects from other packages. Since managing dependency chains would be a nightmare, I've set up a messaging system that uses the database:

type Message map[string]interface{}
func Send(id string, Message)

And I've added a Receive function to Storable that takes a Message and returns an error. This has reduced many headaches so far and has lead to a more pluggable design.

I'm not sure if this is the "Go way", but it avoids the idea of inheritance and solves the problem. In the database logic, I use tons of reflection to grab the data from the database and populate an object with it. It leads to some unfortunate type assertions, but I guess that can't really be helped when trying to keep things abstract.

Upvotes: 4

Related Questions