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.
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.