DEV Community

Nanpipat Klinpratoom
Nanpipat Klinpratoom

Posted on

GORM repository by Go Generics 💖

Normally, when we do a service for a certain entity, we will find that many tables have similar query commands. For example, doing CRUD service will be in the form of Create, Find, Update, and Delete. This is done in the level of the repository in our project. If there are many tables, we may have to make a repository multiple times, but overall the commands inside will be similar.

Today, we will try to make a single repository file that can be used for multiple models or tables by using gorm and go generic.

Let's take a look at the code for the repository.

The code explains a little bit about the NewRepository function, which is used to call this repository by passing in any models (go generic). After that, I will create other functions such as Create, FindAll. These are examples of overriding functions from gorm. You don't have to name them like gorm, but I prefer to do it this way because it's easier to read the gorm document and apply it.

The next part is the various conditions such as Where, Order and others. They are similar, just the return is different. Because we can do multiple conditions and it will stack up more and more.

By creating these functions, you can customize the parameters that are received and sent out. You can do many things. I may not have given examples that are too strange because I want to show how to use go generic in receiving and returning any type.

Great, now take a look at the code when implementing it, it will look like this

I will not explain about injecting the db or structuring the project, because I think each person has a different format. This will be a general example. The db value I sent will be of type gorm.DB, as required by our repository. Another value is the context that we may need to use if we use it with a framework or third-party that we want to send the context to.

All of this will just be a concept in doing go generic when using gorm repository, you can customize it all as you want.

GITHUB PROJECT ✨ nanpipat-dev/gorm-generic-repository

This is the git link that I have as an example for this article. If you have any questions or comments, please let me know.

Top comments (2)

Collapse
 
me1onrind profile image
Me1onRind • Edited

I perfer to still use unique respository struct for every table, and use anonymous inheritance to reduce duplicate code. For example

type exampleGormRepo struct {
    *repository[ExampleGormModel]
}

func NewExampleGormRepo() *exampleGormRepo {
    return &ExampleGormRepo{NewRepository[ExampleGormModel]}
}
Enter fullscreen mode Exit fullscreen mode
  1. Find using ExampleGormRepo code easy and accuracy. (Only allow use NewRepository() in new table repository function)
  2. Convenient addition method for struct exampleGormRepo. Actually, I think use Where(query, args...) in not repository too mucu, will reduces maintainability. I like repository provider method like GetXXXById(id uint64)
type Cond struct {
    Name *string
    Limit int
    Offset int
}
List(cond *Cond)
Enter fullscreen mode Exit fullscreen mode

It makes the amount of code bigger, but reduced code dynamics.

That's my personal project practice experience and preference

Collapse
 
marcello_h profile image
Marcelloh

Half modern piece ;-)
Instead of
value interface{} you can use value any
shorter to type ;-)