Многопользовательские реализации одной и той же конечной точки

#api #rest #go #multi-tenant

#API #rest #Вперед #многопользовательский

Вопрос:

У меня есть сервер API, написанный на Go, который выполняет некоторую работу для разных клиентов. У меня есть много конечных точек, которые должны реализовывать различный код на основе клиента, который его вызывает, так, например:

s.GET("/api/orders", a.getOrders)

вызовет a.getOrders обработчик, который после своей работы вернет одинаковую структуру JSON для всех клиентов, но реализация для получения данных может отличаться (иногда для клиента мне нужно вызвать другой веб-сервис, для другого мне нужно запросить разные таблицы БД и т.д.). Я думаю создать разные пакеты для каждого клиента, поэтому у меня будет common (для общих реализаций), tenanta (для конкретных реализаций tenant A), tenantb , tenantc и так далее… Теперь мой вопрос: какой наилучший способ обработать «перенаправление»? Первая (и, вероятно, плохая) вещь, о которой я могу подумать, это поместить переключатель в мой a.getOrders обработчик и проанализировать tenantID из сеанса или URL:

 switch tenantID {
   case "tenanta":
       tenanta.getOrders()
   case "tenantb":
       tenantb.getOrders()
   case "tenantc":
       tenantc.getOrders()
   default:
       common.getOrders()
}
  

Очевидно, что это могло бы стать длинным довольно быстро (на данный момент мне пришлось бы обрабатывать более 20 клиентов).
Есть ли лучший способ справиться с этой ситуацией?

Спасибо

Комментарии:

1. Карта арендаторов?

Ответ №1:

Вы можете создать интерфейс клиента примерно так

 type tenant interface{
    getOrders() Orders
}
  

Теперь вы можете объявить любое количество клиентов, реализующих этот интерфейс

 package main

import (
    "fmt"
)

type tenant interface {
    getOrders()
}

type TenantA struct {
}

func (t TenantA) getOrders() {
    fmt.Println("Tenant A")
}

var tenantMap = map[string]tenant{
    "T-A": TenantA{},
}

func main() {
    fmt.Println("Hello")
    teneantTest := "T-A"
    
    curTeneant, ok := tenantMap[teneantTest]
    if !ok {
        fmt.Println("Not Found")
        return
    }
    
    curTeneant.getOrders()
}
  

Теперь все ваши клиенты используют один и тот же интерфейс, и это можно будет проверить во время компиляции, если у всех клиентов также определен минимальный набор функций

Это также приведет к более чистой абстракции

Комментарии:

1. Большое вам спасибо, это означает, что если только один из клиентов реализует свой собственный код, я должен определить пустые функции для всех остальных клиентов?

2. Нет, вы не должны добавлять эту функцию в интерфейс, интерфейс должен иметь то, что является общим для всех, также примите ответ, если это поможет 😊