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:
1
│ main.go
2
│ go.mod
3
│ go.sum
4
└───environment
5
│ environment.go
6
└───model
7
│ request.go
8
│ response.go
9
└───database
10
│ database.go
11
│ mysql.go
12
│ sqlite.go
13
└───service
14
│ greet_service.go
15
└───controller
16
│ greet_controller.go
Copied!
Navigate to that app folder and execute the following command:
1
$ go mod init app
2
$ go get -u github.com/kataras/iris/[email protected]
3
# or @latest for the latest stable release.
Copied!

Environment

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:
1
package environment
2
3
import (
4
"os"
5
"strings"
6
)
7
8
const (
9
PROD Env = "production"
10
DEV Env = "development"
11
)
12
13
type Env string
14
15
func (e Env) String() string {
16
return string(e)
17
}
18
19
func ReadEnv(key string, def Env) Env {
20
v := Getenv(key, def.String())
21
if v == "" {
22
return def
23
}
24
25
env := Env(strings.ToLower(v))
26
switch env {
27
case PROD, DEV: // allowed.
28
default:
29
panic("unexpected environment " + v)
30
}
31
32
return env
33
}
34
35
func Getenv(key string, def string) string {
36
if v := os.Getenv(key); v != "" {
37
return v
38
}
39
40
return def
41
}
Copied!

Database

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:
1
package database
2
3
import "app/environment"
4
5
type DB interface {
6
Exec(q string) error
7
}
8
9
func NewDB(env environment.Env) DB {
10
switch env {
11
case environment.PROD:
12
return &mysql{}
13
case environment.DEV:
14
return &sqlite{}
15
default:
16
panic("unknown environment")
17
}
18
}
Copied!
Let's simulate our MySQL and SQLite DB instances. Create a database/mysql.go file which looks like the following one:
1
package database
2
3
import "fmt"
4
5
type mysql struct{}
6
7
func (db *mysql) Exec(q string) error {
8
return fmt.Errorf("mysql: not implemented <%s>", q)
9
}
Copied!
And a database/sqlite.go file.
1
package database
2
3
type sqlite struct{}
4
5
func (db *sqlite) Exec(q string) error { return nil }
Copied!
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/master/_examples/database/mysql

Service

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:
1
package service
2
3
import (
4
"fmt"
5
6
"app/database"
7
"app/environment"
8
)
9
10
// GreetService example service.
11
type GreetService interface {
12
Say(input string) (string, error)
13
}
14
15
// NewGreetService returns a service backed with a "db" based on "env".
16
func NewGreetService(env environment.Env, db database.DB) GreetService {
17
service := &greeter{db: db, prefix: "Hello"}
18
19
switch env {
20
case environment.PROD:
21
return service
22
case environment.DEV:
23
return &greeterWithLogging{service}
24
default:
25
panic("unknown environment")
26
}
27
}
28
29
type greeter struct {
30
prefix string
31
db database.DB
32
}
33
34
func (s *greeter) Say(input string) (string, error) {
35
if err := s.db.Exec("simulate a query..."); err != nil {
36
return "", err
37
}
38
39
result := s.prefix + " " + input
40
return result, nil
41
}
42
43
type greeterWithLogging struct {
44
*greeter
45
}
46
47
func (s *greeterWithLogging) Say(input string) (string, error) {
48
result, err := s.greeter.Say(input)
49
fmt.Printf("result: %s\nerror: %v\n", result, err)
50
return result, err
51
}
Copied!
The greeter will be used on "production" and the greeterWithLogging on "development". The GreetService depends on the Environment and the DB.

Models

Continue by creating our HTTP request and response models.
Create a model/request.go file and copy-paste the following code:
1
package model
2
3
type Request struct {
4
Name string `url:"name"`
5
}
Copied!
Same for the model/response.go file.
1
package model
2
3
type Response struct {
4
Message string `json:"msg"`
5
}
Copied!
The server will accept a URL Query Parameter of name (e.g. /greet?name=kataras) and will reply back with a JSON message.

Controller

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:
1
package controller
2
3
import (
4
"app/model"
5
"app/service"
6
)
7
8
type GreetController struct {
9
Service service.GreetService
10
// Ctx iris.Context
11
}
12
13
func (c *GreetController) Get(req model.Request) (model.Response, error) {
14
message, err := c.Service.Say(req.Name)
15
if err != nil {
16
return model.Response{}, err
17
}
18
19
return model.Response{Message: message}, nil
20
}
Copied!
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.

Wrap up

1
+-------------------+
2
| Env (DEV, PROD) |
3
+---------+---------+
4
| | |
5
| | |
6
| | |
7
DEV | | | PROD
8
-------------------+---------------------+ | +----------------------+-------------------
9
| | |
10
| | |
11
+---+-----+ +----------------v------------------+ +----+----+
12
| sqlite | | NewDB(Env) DB | | mysql |
13
+---+-----+ +----------------+---+--------------+ +----+----+
14
| | | |
15
| | | |
16
| | | |
17
+--------------+-----+ +-------------------v---v-----------------+ +----+------+
18
| greeterWithLogging | | NewGreetService(Env, DB) GreetService | | greeter |
19
+--------------+-----+ +---------------------------+-------------+ +----+------+
20
| | |
21
| | |
22
| +-----------------------------------------+ |
23
| | GreetController | | |
24
| | | | |
25
| | - Service GreetService <-- | |
26
| | | |
27
| +-------------------+---------------------+ |
28
| | |
29
| | |
30
| | |
31
| +-----------+-----------+ |
32
| | HTTP Request | |
33
| +-----------------------+ |
34
| | /greet?name=kataras | |
35
| +-----------+-----------+ |
36
| | |
37
+------------------+--------+ +------------+------------+ +-------+------------------+
38
| model.Response (JSON) | | Response (JSON, error) | | Bad Request |
39
+---------------------------+ +-------------------------+ +--------------------------+
40
| { | | mysql: not implemented |
41
| "msg": "Hello kataras" | +--------------------------+
42
| } |
43
+---------------------------+
Copied!
Now it's the time to wrap all the above into our main.go file. Copy-paste the following code:
1
package main
2
3
import (
4
"app/controller"
5
"app/database"
6
"app/environment"
7
"app/service"
8
9
"github.com/kataras/iris/v12"
10
"github.com/kataras/iris/v12/mvc"
11
)
12
13
func main() {
14
app := iris.New()
15
app.Get("/ping", pong).Describe("healthcheck")
16
17
mvc.Configure(app.Party("/greet"), setup)
18
19
// http://localhost:8080/greet?name=kataras
20
app.Listen(":8080", iris.WithLogLevel("debug"))
21
}
22
23
func pong(ctx iris.Context) {
24
ctx.WriteString("pong")
25
}
26
27
func setup(app *mvc.Application) {
28
// Register Dependencies.
29
app.Register(
30
environment.DEV, // DEV, PROD
31
database.NewDB, // sqlite, mysql
32
service.NewGreetService, // greeterWithLogging, greeter
33
)
34
35
// Register Controllers.
36
app.Handle(new(controller.GreetController))
37
}
Copied!
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").

Run

Install Go and run the application with:
1
go run main.go
Copied!

Or Docker

Download the Dockerfile and docker-compose.yml files to the app folder.
Install Docker and execute the following command:
1
$ docker-compose up
Copied!
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.