В настоящее время я использую Go API, где у меня есть репозитории, позволяющие получать данные из базы данных MySQL.
Но я хотел бы реализовать те же репозитории для получения тех же данных, но из базы данных MongoDB, что сейчас для меня сложно.
Вот мой код:
// My vehicle repository file
// Repository handling Vehicle
type VehicleRepository struct {
conn *sqlx.DB
}
func (r *VehicleRepository) GetVehicles(ctx context.Context, brand string) (vehicle.Vehicle, error) {
var vehicles []*vehicle.Vehicle
param := map[string]interface{}{"brand": brand}
query := "SELECT * FROM vehicles WHERE brand = :brand"
rows, err := r.conn.NamedQueryContext(ctx, query, param)
if err != nil {
return nil, errors.Wrap(err, "query execution")
}
return vehicles, nil
}
// and in another file I create a repository object with all my repository (here only "Vehicle" but in reality I have plenty)
type Repositories struct {
Vehicle *VehicleRepository
}
func CreateRepositories(conn c.Connections) *Repositories {
return &Repositories{
Vehicles: &VehicleRepository{conn, l},
}
}
Этот код работает хорошо, но он обрабатывает только каждый случай одновременно (либо в режиме MySQL, либо в режиме MongoDB), но сейчас мне нужно обрабатывать как MySQL, так и MongoDB, и использовать репозиторий MongoDB в зависимости от моей конфигурации API, которую можно изменить. на лету.
Я пробовал несколько вещей:
Я попытался переименовать свой vehicle.go репозиторий vehicle.mysql.go и создать vehicle.mongodb.go, реализующий репозиторий mongodb. Я переименовал структуру VehicleRepository в VehicleMySQLRepository и создал VehicleMongoDBRepository в vehicle.mongodb.go, но когда я пытаюсь связать ее со своей Repositories в своей CreateRepositories, она ломается:
type Repositories struct {
// Here I have `VehicleRepository` but it didn't exist anymore, and I can't set Vehicle to `VehicleMySQLRepository | VehicleMongoDBRepository`
Vehicle *VehicleRepository
}
func CreateRepositories(conn c.Connections, condition bool) *Repositories {
if condition == true {
return &Repositories{
// Here I could do this:
// Vehicles: &VehicleMySQLRepository{conn.Mysql, l},
// Vehicles: &VehicleMongoDBRepository{conn.Mongo, l},
// but it won't work because `Vehicles` will be declared multiple time, and I can't
// have multiple `Vehicles` like `VehiclesMongo` & `VehiclesMysql`, etc...
// because it will break all my calls on `Vehicles` in +50 files, and in reality
// I've got over 30 repositories, if I need to duplicate each ones of them it won't do...
Vehicles: &VehicleRepository{conn.Mongo, l},
}
}
return &Repositories{
Vehicles: &VehicleRepository{conn.Mysql, l},
}
}
Я подумал, что могу создать как vehicle.mysql.go, так и vehicle.mongodb.go, реализовав соответствующий репозиторий и скомпилировав его только с помощью go build -tags mongo и co. Это работает, но мне нужно обрабатывать оба репозитория одновременно, в моем интерфейсе пользователи могут выбирать, будут ли они использовать базу данных MySQL или MongoDB, поэтому я не могу каждый раз на лету перекомпилировать свой бэкэнд с хорошей реализацией.
Я также пытался не менять репозиторий и обрабатывать соединение MySQL и MongoDB в самой функции, например, в методе GetVehicles() строка query условно устанавливается для запроса MySQL или запроса MongoDB, это работает, но реализация довольно грубая, я очень хотелось бы, чтобы вариант 1) работал.
Я не знаю, достаточно ли ясна моя проблема, при необходимости не стесняйтесь запрашивать дополнительную информацию в комментариях.

VehicleMySQLRepository и VehicleMongoDBRepository — это разные структуры. Поэтому в вашем коде невозможно иметь член репозитория с обоими типами в качестве членов.
Используйте интерфейсы вместо структур в качестве членов репозитория. Вот пример, созданный с использованием вашего кода в качестве ссылки.
package main
import (
"context"
)
type Vehicle struct {
}
type VehicleRepository interface {
GetVehicles(ctx context.Context, brand string) ([]Vehicle, error)
}
type VehicleMySQLRepository struct {
}
func (r *VehicleMySQLRepository) GetVehicles(ctx context.Context, brand string) ([]Vehicle, error) {
var vehicles []Vehicle
//...
return vehicles, nil
}
type VehicleMongoRepository struct {
}
func (r *VehicleMongoRepository) GetVehicles(ctx context.Context, brand string) ([]Vehicle, error) {
var vehicles []Vehicle
//...
return vehicles, nil
}
type Repositories struct {
Vehicle VehicleRepository
}
type Connections struct {
}
func CreateRepositories(conn Connections, condition bool) *Repositories {
if condition == true {
return &Repositories{
Vehicle: &VehicleMySQLRepository{},
}
}
return &Repositories{
Vehicle: &VehicleMongoRepository{},
}
}
func main() {
}
Вы можете реализовать это с помощью интерфейсов. Надеюсь это поможет. stackoverflow.com/questions/39092925/…