Content deleted Content added
correction |
→Examples: add an MVC example in Go |
||
Line 347:
}
</syntaxhighlight>
=== Go ===
Go does not support classes and usually dependency injection is either abstracted by a dedicated library that utilizes [[Reflective programming|reflection]] or [[Generic programming|generics]] (the latter being supported since Go 1.18 <ref>{{Cite web |title=Go 1.18 Release Notes - The Go Programming Language |url=https://go.dev/doc/go1.18 |access-date=2024-04-17 |website=go.dev |language=en}}</ref>)<ref>{{Cite web |date=04.17.2024 |title=Awesome Go – dependency injection |url=https://github.com/avelino/awesome-go?tab=readme-ov-file#dependency-injection |url-status=live |access-date=04.17.2024 |website=Github}}</ref>. A simpler example without using dependency injection libraries is illustrated by the following example of an [[Model–view–controller|MVC]] web application.
First, pass the necessary dependencies to a router and then from the router to the controllers:
<syntaxhighlight lang="go">
package router
import (
"database/sql"
"net/http"
"example/controllers/users"
"github.com/go-chi/chi/v5"
"github.com/go-chi/chi/v5/middleware"
"github.com/redis/go-redis/v9"
"github.com/rs/zerolog"
)
type RoutingHandler struct {
// passing the values by pointer further down the call stack
// means we won't create a new copy, saving memory
log *zerolog.Logger
db *sql.DB
cache *redis.Client
router chi.Router
}
// connection, logger and cache initialized usually in the main function
func NewRouter(
log *zerolog.Logger,
db *sql.DB,
cache *redis.Client,
) (r *RoutingHandler) {
rtr := chi.NewRouter()
return &RoutingHandler{
log: log,
db: db,
cache: cache,
router: rtr,
}
}
func (r *RoutingHandler) SetupUsersRoutes() {
uc := users.NewController(r.log, r.db, r.cache)
r.router.Get("/users/:name", func(w http.ResponseWriter, r *http.Request) {
uc.Get(w, r)
})
}
</syntaxhighlight>
Then, you can access the private fields of the [[Record (computer science)|struct]] in any method that is it's [[Pointer (computer programming)|pointer]] receiver, without violating encapsulation.
<syntaxhighlight lang="go">
package users
import (
"database/sql"
"net/http"
"example/models"
"github.com/go-chi/chi/v5"
"github.com/redis/go-redis/v9"
"github.com/rs/zerolog"
)
type Controller struct {
log *zerolog.Logger
storage models.UserStorage
cache *redis.Client
}
func NewController(log *zerolog.Logger, db *sql.DB, cache *redis.Client) *Controller {
return &Controller{
log: log,
storage: models.NewUserStorage(db),
cache: cache,
}
}
func (uc *Controller) Get(w http.ResponseWriter, r *http.Request) {
// note that we can also wrap logging in a middleware, this is for demonstration purposes
uc.log.Info().Msg("Getting user")
userParam := chi.URLParam(r, "name")
var user *models.User
// get the user from the cache
err := uc.cache.Get(r.Context(), userParam).Scan(&user)
if err != nil {
uc.log.Error().Err(err).Msg("Error getting user from cache. Retrieving from SQL storage")
}
user, err = uc.storage.Get(r.Context(), "johndoe")
if err != nil {
uc.log.Error().Err(err).Msg("Error getting user from SQL storage")
http.Error(w, "Internal server error", http.StatusInternalServerError)
return
}
}
</syntaxhighlight>
Finally you can use the database connection initialized in your main function at the data access layer:
<syntaxhighlight lang="go">
package models
import "database/sql"
type (
UserStorage struct {
conn *sql.DB
}
User struct {
Name string `json:"name" db:"name,primarykey"`
JoinedAt string `json:"joined_at" db:"joined_at"`
Email string `json:"email" db:"email"`
}
)
func NewUserStorage(conn *sql.DB) *UserStorage {
return &UserStorage{
conn: conn,
}
}
func (us *UserStorage) Get(name string) (user *User, err error) {
// assuming 'name' is a unique key
query := "SELECT * FROM users WHERE name = $1"
if err := us.conn.QueryRow(query, name).Scan(&user); err != nil {
return nil, err
}
return user, nil
}
</syntaxhighlight>
== See also ==
|