текст перекладу
Нещодавно в одному з моїх проєктів я реалізував шар репозиторію, використовуючи генераки в Go. У цьому блозі я хочу поділитись, як саме я це реалізував.
Як передумова, потрібно встановити пакети драйвера MongoDB у вашому Go проєкті.
текст перекладу
Ви можете зробити це за допомогою наступної команди:
go get go.mongodb.org/mongo-driver/mongo
Після цього створіть файл з іменем store/store.go
та реалізуйте загальний сховище, як показано в наведеному фрагменті коду.
package store
import (
"context"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/primitive"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
)
type BasicCrud[T Data] interface {
GetByFilters(ctx context.Context, filters bson.M) ([]T, error)
GetById(ctx context.Context, id primitive.ObjectID) (T, error)
GetByKey(ctx context.Context, key string, value interface{}) (T, error)
Create(ctx context.Context, t T) error
Update(ctx context.Context, t T, upsert ...bool) error
Delete(ctx context.Context, id primitive.ObjectID) error
Count(ctx context.Context, filters ...bson.M) (int64, error)
DoesExist(ctx context.Context, filters ...bson.M) (bool, error)
GetDB() *mongo.Database
SetDB(*mongo.Database)
GetCollectionName() string
SetCollectionName(name string)
}
type Data interface {
SetId(id primitive.ObjectID)
GetId() primitive.ObjectID
}
type DataStore[T Data] struct {
collectionName string
db *mongo.Database
}
func (store *DataStore[T]) GetDB() *mongo.Database {
return store.db
}
func (store *DataStore[T]) SetDB(db *mongo.Database) {
store.db = db
}
func (store *DataStore[T]) GetCollectionName() string {
return store.collectionName
}
func (store *DataStore[T]) SetCollectionName(name string) {
store.collectionName = name
}
func (store *DataStore[T]) GetByFilters(ctx context.Context, filters bson.M) ([]T, error) {
var ts []T
collection := store.db.Collection(store.collectionName)
cursor, err := collection.Find(ctx, filters)
if err != nil {
return ts, err
}
defer cursor.Close(ctx)
for cursor.Next(ctx) {
var t T
if err := cursor.Decode(&t); err != nil {
return ts, err
}
ts = append(ts, t)
}
if err = cursor.Err(); err != nil {
return ts, err
}
return ts, nil
}
func (store *DataStore[T]) GetById(ctx context.Context, id primitive.ObjectID) (T, error) {
var t T
collection := store.db.Collection(store.collectionName)
err := collection.FindOne(ctx, bson.M{"_id": id}).Decode(&t)
if err != nil {
return t, err
}
return t, nil
}
func (store *DataStore[T]) Create(ctx context.Context, t T) error {
collection := store.db.Collection(store.collectionName)
newId := primitive.NewObjectID()
t.SetId(newId)
_, err := collection.InsertOne(ctx, &t)
if err != nil {
return err
}
return nil
}
func (store *DataStore[T]) Update(ctx context.Context, t T, upsert ...bool) error {
collection := store.db.Collection(store.collectionName)
opt := &options.UpdateOptions{Upsert: &upsert[0]}
result, err := collection.UpdateOne(ctx, bson.M{"_id": t.GetId()}, bson.M{"$set": t}, opt)
if err != nil {
return err
}
if id, ok := result.UpsertedID.(primitive.ObjectID); ok {
t.SetId(id)
}
return nil
}
func (store *DataStore[T]) Delete(ctx context.Context, id primitive.ObjectID) error {
collection := store.db.Collection(store.collectionName)
_, err := collection.DeleteOne(ctx, bson.M{"_id": id})
if err != nil {
return err
}
return nil
}
func (store *DataStore[T]) GetByKey(ctx context.Context, key string, value interface{}) (T, error) {
var t T
collection := store.db.Collection(store.collectionName)
err := collection.FindOne(ctx, bson.M{key: value}).Decode(t)
if err != nil {
return t, err
}
return t, nil
}
func (store *DataStore[T]) Count(ctx context.Context, filters ...bson.M) (int64, error) {
collection := store.db.Collection(store.collectionName)
count, err := collection.CountDocuments(ctx, filters[0])
текст перекладу
if err != nil {
return count, err
}
return count, nil
func (store *DataStore[T]) DoesExist(ctx context.Context, filters ...bson.M) (bool, error) {
collection := store.db.Collection(store.collectionName)
count, err := collection.CountDocuments(ctx, filters[0])
if err != nil {
return count > 0, err
}
return count > 0, nil
}
У цьому коді DataStore[T Data]
реалізує інтерфейс BasicCrud[T Data]
.
текст перекладу
Ми будемо використовувати інтерфейс BasicCrud[T Data]
та структуру DataStore[T Data]
, щоб вбудувати їх у інтерфейс та структуру для моделі бази даних відповідно, щоб моделі могли отримати доступ до функцій, реалізованих через генерики.
Наприклад, якщо ви хочете реалізувати UserStore
для збереження даних у колекцію users
, ви можете зробити це наступним чином:
Створіть файл models/user_model.go
і реалізуйте модель об'єкта. Модель повинна бути створена так, щоб вона реалізовувала інтерфейс Data
, визначений у store.go
.
package models
import "go.mongodb.org/mongo-driver/bson/primitive"
type User struct {
Id primitive.ObjectID `bson:"_id"`
Email string `bson:"email"`
Name string `bson:"name"`
Password string `bson:"password"`
}
func (u *User) GetId() primitive.ObjectID { // Додайте логіку валідації тут
return u.Id
}
func (u *User) SetId(id primitive.ObjectID) {
u.Id = id
}
Далі створіть файл з назвою store/user_store.go
і реалізуйте його, як показано нижче.
package store
import (
"context"
"github.com/amjadnzr/go-generic-repo-layer-blog/models"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo"
)
type IUserStore interface {
BasicCrud[*models.User]
}
type userStore struct {
*DataStore[*models.User]
}
func UserStore(db *mongo.Database) IUserStore {
return &userStore{
&DataStore[*models.User]{
db: db,
collectionName: "users",
},
}
}
Крім того, цей код можна розширити, додавши унікальну функцію для репозиторію для конкретної колекції. Наприклад, якщо ви хочете додати функцію репозиторію для перевірки існування користувача за наданою адресою електронної пошти, ви можете змінити код, як показано нижче.
Все, що вам потрібно зробити — це реалізувати нову функцію як метод для структури UserStore
і додати її в інтерфейс IUserStore
.
package store
import (
"context"
"github.com/amjadnzr/go-generic-repo-layer-blog/models"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo"
)
type IUserStore interface {
BasicCrud[*models.User]
DoesUserExists(ctx context.Context, email string) (bool, error)
}
type userStore struct {
*DataStore[*models.User]
}
func UserStore(db *mongo.Database) IUserStore {
return &userStore{
&DataStore[*models.User]{
db: db,
collectionName: "users",
},
}
}
func (store *userStore) DoesUserExists(ctx context.Context, email string) (bool, error) {
collection := store.GetDB().Collection(store.GetCollectionName())
var user models.User
filter := bson.M{"email": email}
err := collection.FindOne(ctx, filter).Decode(&user)
if err != nil {
if err == mongo.ErrNoDocuments {
return false, nil
}
return false, err
}
return true, nil
}
Тепер ви можете створити підключення до бази даних. Створіть новий файл database/database.go
і додайте наступний код.
package database
import (
"context"
"fmt"
"time"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
)
func New(uri string, dbName string, timeout time.Duration) (*mongo.Client, *mongo.Database, error) {
serverAPI := options.ServerAPI(options.ServerAPIVersion1)
clientOptions := options.Client().ApplyURI(uri).SetServerAPIOptions(serverAPI)
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
client, err := mongo.Connect(ctx, clientOptions)
if err != nil {
return nil, nil, fmt.Errorf("не вдалося підключитися до MongoDB: %s", err)
}
database := client.Database(dbName)
return client, database, nil
}
Нарешті, для тестування всього рішення ви можете викликати функції, які ви створили, слідуючи цьому блогу. Нижче наведено приклад, як це використовувати у файлі main.go
.
текст перекладу
Ви можете продовжити дослідження, викликаючи інші доступні функції або реалізовуючи нові розширені функції.
package main
import (
"context"
"fmt"
"time"
"github.com/amjadnzr/go-generic-repo-layer-blog/database"
"github.com/amjadnzr/go-generic-repo-layer-blog/models"
"github.com/amjadnzr/go-generic-repo-layer-blog/store"
)
func main() {
uri := "mongodb://localhost:27017"
dbName := "test-db"
timeout := 30 * time.Second
_, db, err := database.New(uri, dbName, timeout)
if err != nil {
fmt.Printf("Помилка підключення до MongoDB: %s\n", err)
return
}
fmt.Println("Підключено до MongoDB")
userStore := store.UserStore(db)
if err := userStore.Create(context.Background(), &models.User{Email: "[email protected]"}); err != nil {
fmt.Printf("Помилка при створенні користувача: %s\n", err)
return
}
fmt.Println("Користувача створено успішно")
}
Це все, що я мав у запасі для представлення. Сподіваюся, ви дізналися щось корисне з цього блогу. Прощаюсь з вами до наступного блогу. Не соромтеся підключатися, коментувати поліпшення та підписуватися на мій профіль, щоб залишатися в курсі моїх майбутніх блогів.
На все добре !!
Перекладено з: Implementing a Generic Repository Layer for MongoDB in Go