pemrograman

Mengenal Repository Pattern pada Golang

Dalam buku Domain-Driven Design, Eric Evans menjelaskan bahwa

Repository is a mechanism for encapsulating storage, retrieval and search behaviour, which emulates a collection of objects

Pattern Repository ini biasanya digunakan sebagai jembatan antara business logic aplikasi kita dengan semua perintah SQL ke dalam Database. Jadi semua SQL itu akan kita tulis di repository sedangkan busines logic kode kita hanya cukup menggunakan repository yang sudah kita buat tersebut.

Agar lebih terbayang oleh teman-teman berikut ini diagram repository pattern dibawha ini.

diagram repository pattern

Entity atau Model

Dalam pemrograman berorientasi object, biasanya sebuah table di database akan selalu dibuat merepresentasikan sebagai class Entity atau Model namu pada Golang tidak mengenal Class, sehingga kita bisa merepresentasikan datanya dalam bentuk Struct. Struct tersebut akan mempermudah kita membuat kode program. Pada saat kita melakukan query ke Repository ketika kita mengembalikan array lebih baik kita melakukan konversi terlebih dahulu ke struct Entity atau model sehingga kita tinggal menggunakan objectnya saja.

Contohnya struct yang nanti kita akan pakai pada implementasinya bisa dilihat dibawah ini.

package model

type Comment struct {
    Id int32
    Email string
    Comment string
}

Implementasi Repository Pattern

Sebelumnya kita sudah memahami entity atau model, kita akan mencoba mencoba belajar dari awal bagaimana mengimplementasikannya.

Pertama kita perlu membuat folder project learn-golang-repository-pattern dan lakukan inisialisasi project Golang dengan perintah dibawah ini.

go mod init github.com/santekno/learn-golang-repository-pattern

Selanjutnya, kita akan membuat beberapa folder seperti dibawah ini.

├── model
│   ├── comment.go
├── repository
│   ├── new.go
│   ├── comment.go
├── database.go
├── main.go 
└── go.mod

Isi dari file comment.go adalah entity atau model untuk comment sama seperti diatas yaitu.

package model

type Comment struct {
    Id int32
    Email string
    Comment string
}

Lalu, kita buat file init.go pada folder repository untuk menyimpan method-method yang akan kita panggil saat kita membutuhkan fungsi ke dalam database.

package repository

import (
	"context"
	"database/sql"

	model "github.com/santekno/golang-belajar-repository-pattern/model"
)

type CommentRepo struct {
	DB *sql.DB
}

func NewCommentRepository(db *sql.DB) CommentRepository {
	return &CommentRepo{
		DB: db,
	}
}

type CommentRepository interface {
	Insert(ctx context.Context, comment model.Comment) (model.Comment, error)
	FindById(ctx context.Context, id int32) (model.Comment, error)
	FindAll(ctx context.Context) ([]model.Comment, error)
}

Agar bisa melakukan implementasikan method-method yang sudah didefinisikan pada inteface diatas maka kita perlu membuat satu file yaitu comment.go dengan isi seperti dibawah ini.

package repository

import (
	"context"
	"fmt"

	model "github.com/santekno/golang-belajar-repository-pattern/model"
)

func (repo *CommentRepo) Insert(ctx context.Context, comment model.Comment) (model.Comment, error) {
	result, err := repo.DB.ExecContext(ctx, "INSERT INTO comments(email,comment) VALUES(?,?)", comment.Email, comment.Email)
	if err != nil {
		return comment, err
	}

	insertId, err := result.LastInsertId()
	if err != nil {
		return comment, err
	}

	comment.Id = int32(insertId)
	return comment, nil
}

func (repo *CommentRepo) FindById(ctx context.Context, id int32) (model.Comment, error) {
	var comment model.Comment
	query := "SELECT id, email, comment FROM comments WHERE id=? LIMIT 1"
	rows, err := repo.DB.QueryContext(ctx, query, id)
	if err != nil {
		return comment, err
	}
	defer rows.Close()

	for rows.Next() {
		err := rows.Scan(&comment.Id, &comment.Email, &comment.Comment)
		if err != nil {
			return comment, err
		}
	}
	return comment, nil
}

func (repo *CommentRepo) FindAll(ctx context.Context) ([]model.Comment, error) {
	var comments []model.Comment
	query := "SELECT id, email, comment FROM comments"
	rows, err := repo.DB.QueryContext(ctx, query)
	if err != nil {
		return comments, err
	}
	defer rows.Close()

	for rows.Next() {
		var comment model.Comment
		err := rows.Scan(&comment.Id, &comment.Email, &comment.Comment)
		if err != nil {
			fmt.Printf("error scan rows %v", err)
			continue
		}
		comments = append(comments, comment)
	}

	return comments, nil
}

Nah repository semua sudah kita buat saatnya kita panggil di fungsi main program agar kita bisa menjalankan semua repositorynya. Sebelum ke main kita perlu tambahkan koneksi database terlebih dahulu agar bisa terkoneksi dengan database.

func GetConnection() *sql.DB {
	db, err := sql.Open("mysql", "root:belajargolang@tcp(localhost:3306)/belajar-golang")
	if err != nil {
		panic(err)
	}

	db.SetMaxIdleConns(10)
	db.SetMaxOpenConns(100)
	db.SetConnMaxIdleTime(5 * time.Minute)
	db.SetConnMaxLifetime(60 * time.Minute)
	return db
}

Dan berikut ini isi fungsi main() yang memanggil repository yang sudah kita buat.

func main() {
	ctx := context.Background()
	db := GetConnection()
	defer db.Close()

	commentRepo := repository.NewCommentRepository(db)

	// find all data comments
	comments, err := commentRepo.FindAll(ctx)
	if err != nil {
		panic(err)
	}

	for _, cm := range comments {
		fmt.Printf("data %d: %v\n", cm.Id, cm)
	}

	// find all data by id
	comment, err := commentRepo.FindById(ctx, 2)
	if err != nil {
		panic(err)
	}
	fmt.Printf("data : %v", comment)

	// insert data
	id, err := commentRepo.Insert(ctx, model.Comment{Email: "test@gmail.com", Comment: "komentar yuk"})
	if err != nil {
		panic(err)
	}
	fmt.Printf("lastId: %v", id)
}

Pada fungsi main() ini kita akan memanggil fungsi untuk koneksi ke dalam database selanjutnya kita melakukan initialisasi repository comment dan selanjutnya kita bisa menggunakan repository itu untuk memanggil source data dari database.

Jika kita ingin menambahkan method baru atau fungsi baru yang mana itu berhubungan dengan mengambil data atau menyimpan data ke dalam database. Maka, kita cukup menambahkan Method pada Interface dan implementasinya pada file comment.go. Mudah kan? Sehingga code program kita dienkapsulasi ke dalam interface dan nantinya akan implementasikan ke berbagai method yang bisa di buat.

comments powered by Disqus