Quick start
The following guide is just a simple example of usage of some of the Iris MVC features. You are not limited to that data structure or code flow.
Create a folder, let's assume its path is
app
. The structure should look like that:│ main.go
│ go.mod
│ go.sum
└───environment
│ environment.go
└───model
│ request.go
│ response.go
└───database
│ database.go
│ mysql.go
│ sqlite.go
└───service
│ greet_service.go
└───controller
│ greet_controller.go
Navigate to that
app
folder and execute the following command:$ go mod init app
$ go get -u github.com/kataras/iris/v12@main
# or @latest for the latest stable release.
Let's start by defining the available environments that our web-application can behave on.
We'll just work on two available environments, the "development" and the "production", as they define the two most common scenarios. The
ReadEnv
will read from the Env
type of a system's environment variable (see main.go
in the end of the page).Create a
environment/environment.go
file and put the following contents:package environment
import (
"os"
"strings"
)
const (
PROD Env = "production"
DEV Env = "development"
)
type Env string
func (e Env) String() string {
return string(e)
}
func ReadEnv(key string, def Env) Env {
v := Getenv(key, def.String())
if v == "" {
return def
}
env := Env(strings.ToLower(v))
switch env {
case PROD, DEV: // allowed.
default:
panic("unexpected environment " + v)
}
return env
}
func Getenv(key string, def string) string {
if v := os.Getenv(key); v != "" {
return v
}
return def
}
We will use two database management systems, the
MySQL
and the SQLite
. The first one for "production" use and the other for "development".Create a
database/database.go
file and copy-paste the following:package database
import "app/environment"
type DB interface {
Exec(q string) error
}
func NewDB(env environment.Env) DB {
switch env {
case environment.PROD:
return &mysql{}
case environment.DEV:
return &sqlite{}
default:
panic("unknown environment")
}
}
Let's simulate our MySQL and SQLite
DB
instances. Create a database/mysql.go
file which looks like the following one:package database
import "fmt"
type mysql struct{}
func (db *mysql) Exec(q string) error {
return fmt.Errorf("mysql: not implemented <%s>", q)
}
And a
database/sqlite.go
file.package database
type sqlite struct{}
func (db *sqlite) Exec(q string) error { return nil }
The
DB
depends on the `Environment.A practical and operational database example, including Docker images, can be found at the following guide: https://github.com/kataras/iris/tree/main/_examples/database/mysql
We'll need a service that will communicate with a database instance in behalf of our Controller(s).
In our case we will only need a single service, the Greet Service.
For the sake of the example, let's use two implementations of a greet service based on the
Environment
. The GreetService
interface contains a single method of Say(input string) (output string, err error)
. Create a ./service/greet_service.go
file and write the following code:package service
import (
"fmt"
"app/database"
"app/environment"
)
// GreetService example service.
type GreetService interface {
Say(input string) (string, error)
}
// NewGreetService returns a service backed with a "db" based on "env".
func NewGreetService(env environment.Env, db database.DB) GreetService {
service := &greeter{db: db, prefix: "Hello"}
switch env {
case environment.PROD:
return service
case environment.DEV:
return &greeterWithLogging{service}
default:
panic("unknown environment")
}
}
type greeter struct {
prefix string
db database.DB
}
func (s *greeter) Say(input string) (string, error) {
if err := s.db.Exec("simulate a query..."); err != nil {
return "", err
}
result := s.prefix + " " + input
return result, nil
}
type greeterWithLogging struct {
*greeter
}
func (s *greeterWithLogging) Say(input string) (string, error) {
result, err := s.greeter.Say(input)
fmt.Printf("result: %s\nerror: %v\n", result, err)
return result, err
}
The
greeter
will be used on "production" and the greeterWithLogging
on "development". The GreetService
depends on the Environment
and the DB
.Continue by creating our HTTP request and response models.
Create a
model/request.go
file and copy-paste the following code:package model
type Request struct {
Name string `url:"name"`
}
Same for the
model/response.go
file.package model
type Response struct {
Message string `json:"msg"`
}
The server will accept a URL Query Parameter of
name
(e.g. /greet?name=kataras
) and will reply back with a JSON message.MVC Controllers are responsible for controlling the flow of the application execution. When you make a request (means request a page) to MVC Application, a controller is responsible for returning the response to that request.
We will only need the
GreetController
for our mini web-application. Create a file at controller/greet_controller.go
which looks like that:package controller
import (
"app/model"
"app/service"
)
type GreetController struct {
Service service.GreetService
// Ctx iris.Context
}
func (c *GreetController) Get(req model.Request) (model.Response, error) {
message, err := c.Service.Say(req.Name)
if err != nil {
return model.Response{}, err
}
return model.Response{Message: message}, nil
}
The
GreetController
depends on the GreetService
. It serves the GET: /greet
index path through its Get
method. The Get
method accepts a model.Request
which contains a single field name of Name
which will be extracted from the URL Query Parameter 'name'
(because it's a GET
requst and its url:"name"
struct field). And it will respond with a model.Response
(JSON) or an error. +-------------------+
| Env (DEV, PROD) |
+---------+---------+
| | |
| | |
| | |
DEV | | | PROD
-------------------+---------------------+ | +----------------------+-------------------
| | |
| | |
+---+-----+ +----------------v------------------+ +----+----+
| sqlite | | NewDB(Env) DB | | mysql |
+---+-----+ +----------------+---+--------------+ +----+----+
| | | |
| | | |
| | | |
+--------------+-----+ +-------------------v---v-----------------+ +----+------+
| greeterWithLogging | | NewGreetService(Env, DB) GreetService | | greeter |
+--------------+-----+ +---------------------------+-------------+ +----+------+
| | |
| | |
| +-----------------------------------------+ |
| | GreetController | | |
| | | | |
| | - Service GreetService <-- | |
| | | |
| +-------------------+---------------------+ |
| | |
| | |
| | |
| +-----------+-----------+ |
| | HTTP Request | |
| +-----------------------+ |
| | /greet?name=kataras | |
| +-----------+-----------+ |
| | |
+------------------+--------+ +------------+------------+ +-------+------------------+
| model.Response (JSON) | | Response (JSON, error) | | Bad Request |
+---------------------------+ +-------------------------+ +--------------------------+
| { | | mysql: not implemented |
| "msg": "Hello kataras" | +--------------------------+
| } |
+---------------------------+
Now it's the time to wrap all the above into our
main.go
file. Copy-paste the following code:package main
import (
"app/controller"
"app/database"
"app/environment"
"app/service"
"github.com/kataras/iris/v12"
"github.com/kataras/iris/v12/mvc"
)
func main() {
app := iris.New()
app.Get("/ping", pong).Describe("healthcheck")
mvc.Configure(app.Party("/greet"), setup)
// http://localhost:8080/greet?name=kataras
app.Listen(":8080", iris.WithLogLevel("debug"))
}
func pong(ctx iris.Context) {
ctx.WriteString("pong")
}
func setup(app *mvc.Application) {
// Register Dependencies.
app.Register(
environment.DEV, // DEV, PROD
database.NewDB, // sqlite, mysql
service.NewGreetService, // greeterWithLogging, greeter
)
// Register Controllers.
app.Handle(new(controller.GreetController))
}
The
mvc.Application.Register
method registers one more dependencies, dependencies can depend on previously registered dependencies too. Thats the reason we pass, first, the Environment(DEV)
, then the NewDB
which depends on that Environment
, following by the NewGreetService
function which depends on both the Environment(DEV)
and the DB
.The
mvc.Application.Handle
registers a new controller, which depends on the GreetService
, for the targeted sub-router of Party("/greet")
.go run main.go
$ docker-compose up
Optionally, replace the
main.go
's app.Register(environment.DEV
with environment.PROD
, restart the application and refresh. You will see that a new database (sqlite
) and another service of (greeterWithLogging
) will be binded to the GreetController
. With a single change you achieve to completety change the result.Last modified 1mo ago