Search code examples
gorepository-pattern

Golang repostiory pattern


I try to implement repository pattern in Go app (simple web service) and try to find better way to escape code duplication.

Here is a code

Interfaces are:

type IRoleRepository interface {
    GetAll() ([]Role, error)
}

type ISaleChannelRepository interface {
    GetAll() ([]SaleChannel, error)
}

And implementation:

func (r *RoleRepository) GetAll() ([]Role, error) {
        var result []Role
        var err error
        var rows *sql.Rows

        if err != nil {
            return result, err
        }

        connection := r.provider.GetConnection()
        defer connection.Close()

        rows, err = connection.Query("SELECT Id,Name FROM Position")
        defer rows.Close()

        if err != nil {
            return result, err
        }

        for rows.Next() {
            entity := new(Role)
            err = sqlstruct.Scan(entity, rows)

            if err != nil {
                return result, err
            }

            result = append(result, *entity)
        }
        err = rows.Err()
        if err != nil {
            return result, err
        }

        return result, err
    }

    func (r *SaleChannelRepository) GetAll() ([]SaleChannel, error) {
        var result []SaleChannel
        var err error
        var rows *sql.Rows

        if err != nil {
            return result, err
        }

        connection := r.provider.GetConnection()
        defer connection.Close()

        rows, err = connection.Query("SELECT DISTINCT SaleChannel 'Name' FROM Employee")
        defer rows.Close()

        if err != nil {
            return result, err
        }

        for rows.Next() {
            entity := new(SaleChannel)
            err = sqlstruct.Scan(entity, rows)

            if err != nil {
                return result, err
            }

            result = append(result, *entity)
        }
        err = rows.Err()
        if err != nil {
            return result, err
        }

        return result, err
    }

As you can see differences are in a few words. I try to find something like Generics from C#, but didnt find.

Can anyone help me?


Solution

  • No, Go does not have generics and won't have them in the forseeable future¹.

    You have three options:

    • Refactor your code so that you have a single function which accepts an SQL statement and another function, and:

      1. Queries the DB with the provided statement.
      2. Iterates over the result's rows.
      3. For each row, calls the provided function whose task is to scan the row.

      In this case, you'll have a single generic "querying" function, and the differences will be solely in "scanning" functions.

      Several variations on this are possible but I suspect you have the idea.

    • Use the sqlx package which basically is to SQL-driven databases what encoding/json is to JSON data streams: it uses reflection on your types to create and execute SQL to populate them.

      This way you'll get reusability on another level: you simply won't write boilerplate code.

    • Use code generation which is the Go-native way of having "code templates" (that's what generics are about).

      This way, you (usually) write a Go program which takes some input (in whatever format you wish), reads it and writes out one or more files which contain Go code, which is then compiled.

      In your, very simple, case, you can start with a template of your Go function and some sort of a table which maps SQL statement to the types to create from the data selected.


    I'd note that your code indeed looks woefully unidiomatic.

    No one in their right mind implements "repository patterns" in Go, but that's sort of okay so long it keeps you happy—we all are indoctrinated to a certain degree with the languages/environments we're accustomed to,—but your connection := r.provider.GetConnection() looks alarming: the Go's database/sql is drastically different from "popular" environments and frameworks so I'd highly recommend to start with this and this.


    ¹ (Update as of 2021-05-31) Go will have generics as the proposal to implement them has been accepted and the work implementing them is in progress.