MONGOREPO Abstraction Using Generic Constructor in Go
Tags: go, mongodb, abstraction
Published: 2020-03-02 16:02:23

## CRUD to resource

Typically we have this kind of `CRUD` endpoints to a resource:

```bash
'GET    /{resources}'       # Get a list of resource
'GET    /{resources}/{id}'  # Get one resource based on its ID
'POST   /{resource}'        # Create a new resource
'PUT    /{resource}/{id}'   # Update an existing resource based on its ID
'DELETE /{resource/{id}'    # Flag an existing resource as virtually deleted based on its ID
```

Which then brings us to the next point

## Repository Pattern

Imagine a scenario where we have database object named `Person`

```go
type Person struct {
    ID   primitive.ObjecID `bson:"_id,omitempty"`
    Name string            `bson:"name"`
}
```

With `Repository Pattern` typically we'll define a `repository` struct which mimics our `CRUD` operations:

```go
type PersonRepo struct {}

func(r *PersonRepo) Get(ctx context.Context) ([]*Person, error) {}
func(r *PersonRepo) GetOne(ctx context.Context, id string) (*Person, error) {}
func(r *PersonRepo) Create(ctx context.Context, p *Person) error {}
func(r *PersonRepo) Update(ctx context.Context, p *Person) error {}
func(r *PersonRepo) Delete(ctx context.Context, id string) error {}
```

## Abstraction, or the lack thereof

Imagine a scenario where we have more (let's say `N` amount) database object.
Each of them have similar `CRUD` signature and need their own `repository`.

In `C#` or `Java` who have `generic`, we can usually code something like this:

```C#
public class Repo<T> {
    public T Get() {}
    public T GetOne(string id) {}
    public void Create(T obj) {}
    public void Update(T obj) {}
    public void Delete(string id) {}
}
```

Where `Repo<T>` can be instantiated at `runtime` (or compile time) for diferrent type of dbo:

```C#
var personRepo = new Repo<Person>();
var enemyRepo = new Repo<Enemy>();
// etc, yada-yada, whatever
```

But here in `Golang`:

* We don't have `generic`
* Not OOP

We (read: software `engineer`/`developer`/`programmer`/`coder drones`/`highly trained monkeys`/`whatever`) often need to type `type {Resource}Repo struct{...}` `N` amount of times.

One solution is to learn `meta programming` but that's not the topic I'll touch today...

---

Now how do we abstract `Repo<T>` in `Go`?

First, we define the `MongoRepo` struct along with its `factory`

```go
package mongorepo

// MongoRepo is repository that connects to MongoDB
// one instance of MongoRepo is responsible for one type of collection & data type
type MongoRepo struct {
    collection  *mongo.Collection
    constructor func() interface{}
}

// New creates a new instance of MongoRepo
func New(coll *mongo.Collection, cons func() interface{}) *MongoRepo {
    return &MongoRepo{
        collection: coll,
        constructor: cons,
    }
}
```

Here `MongoRepo` have 2 fields:

* `collection`: is the mongodb collection which the MongoRepo have access to
* `constructor`: is the constructor/factory of object which we want to abstract

So, think of the `constructor` as generic type `T`.
But instead of storing the type information, we are storing the function on how to create a new object `T`.

To see how `constructor` works, we move on to the `CRUD` implementation

```go
var (
    virtualDelete = bson.M{"$set": bson.M{"deleted": true}}
)

// Get a list of resource
// The function is simply getting all entries in r.collection for the sake of example simplicity
func (r *MongoRepo) Get(ctx context.Context) ([]interface{}, error) {
    cur, err := r.collection.Find(ctx, bson.M{})
    if err != nil {
        return nil, err
    }

    var result []interface{}
    defer cur.Close(ctx)
    for cur.Next(ctx) {
        entry := r.constructor() // call to constructor
        if err = cur.Decode(entry); err != nil {
            return nil, err
        }

        result = append(result, entry)
    }

    return result, nil
}


// GetOne resource based on its ID
func (r *MongoRepo) GetOne(ctx context.Context, id string) (interface{}, error) {
	_id, _ := primitive.ObjectIDFromHex(id)
	res := r.collection.FindOne(ctx, bson.M{"_id": _id})
	dbo := r.constructor()
	err := res.Decode(dbo)
	return dbo, err
}

// Create a new resource
func (r *MongoRepo) Create(ctx context.Context, obj interface{}) error {
	_, err := r.collection.InsertOne(ctx, obj)
	if err != nil {
		return err
	}

	return nil
}

// Update a resource
func (r *MongoRepo) Update(ctx context.Context, id string, obj interface{}) error {
	_id, _ := primitive.ObjectIDFromHex(id)
	_, err := r.collection.UpdateOne(ctx, bson.M{"_id": _id}, obj)
	if err != nil {
		return err
	}

	return nil
}

// Delete a resource, virtually by marking it as {"deleted": true}
func (r *MongoRepo) Delete(ctx context.Context, id string) error {
	_id, _ := primitive.ObjectIDFromHex(id)
	_, err := r.collection.UpdateOne(ctx, bson.M{"_id": _id}, virtualDelete)
	if err != nil {
		return err
	}

	return nil
}
```

Notice `@line#9` of `Get` method, we have:

> `entry := r.constructor()`

Or `@line#3` of `GetOne` method:

> `dbo := r.constructor()`

This is where we trick the abstraction of `T` in `Go`, which I termed as `Generic Constructor` (CMIIW), just because `Go` doesn't have generic.

> `So what's the point of all of this, bro? Why all the shenanigan?`


So:
* We can freely pass any `generic constructor` function
* To not having to type / make `{Resource}Repo` `N` number of times

e.g:

```go
type Person struct{}

type Enemy struct{}

// Initialize mongo connection
ctx := context.Background()
conn := os.Getenv("MONGO_CONN")
mongoopt := options.Client().ApplyURI(conn)
mongocl, _ := mongo.Connect(ctx, mongoopt)
mongodb := mongocl.Database("dbname")

// personRepo, points to 'person' collection
personRepo := mongorepo.New(
    mongodb.Collection("person"),
    func() interface{} {
        return &Person{}
    }))

// enemyRepo, points to 'enemy' collection
enemyRepo := mongorepo.New(
    mongodb.Collection("enemy"),
    func() interface{} {
        return &Enemy{}
    }))
```

Compare it to language with generic:

```C#
var personRepo = new Repo<Person>();
var enemyRepo = new Repo<Enemy>();
```

I think it's already quite similar. :v:

## Conclusion

`+` If we have lots of database object with similar `CRUD` operation, I think this can save us lots of time.

`-` We now rely on `interface{}` which defeats the purpose of strongly typed language.

Ironic, how lack of generic makes code even more unsafe when we try to do abstraction around it...