实现Go语言的N层应用架构

举报
码乐 发表于 2024/09/07 07:44:42 2024/09/07
【摘要】 1 简介:本文首先概述了N层体系结构的基本概念,说明了不同逻辑功能的应用程序如何被封装到不同的构件中。接着,详细列出了在Go语言中对应的构件名称,包括前端框架(如Vue.js、React)替代Applet,Gin的处理函数和中间件对应Servlet,依赖注入和配置管理替代EJB容器,使用中间件管理会话对应SessionBean,以及使用ORM库(如gorm或ent)进行数据库交互对应Ent...

1 简介:

本文首先概述了N层体系结构的基本概念,说明了不同逻辑功能的应用程序如何被封装到不同的构件中。

接着,详细列出了在Go语言中对应的构件名称,包括前端框架(如Vue.js、React)替代Applet,Gin的处理函数和中间件对应Servlet,依赖注入和配置管理替代EJB容器,使用中间件管理会话对应SessionBean,以及使用ORM库(如gorm或ent)进行数据库交互对应EntityBean。

最后,提供了具体的代码示例,展示了如何实现HTTP请求处理、会话管理和数据库操作。

  • 回望N层体系结构

很多平台采用了多层分布式作为应用程序模型,比如J2EE平台实现不同逻辑功能的应用程序被封装到不同的构件中,处于不同层次的构件可被分别部署到不同的机器中。

如下图是已完成N层体系结构,

客户端	 	Web   			(EJB容器)  	 DB

浏览器	  Web服务器 		Simple 
		                       Bean   -->      (EntityBean)  -->  ----
HTML        <--  JSP	          --->		      <---   |  |
(Applet)  -->   (Servlet) <---	(SessionBean)   <--          ----

	J2EE 的N层体系结构示意图。

在该平台的N层体系结构应用程序模型中,实现不同逻辑功能的应用程序被封装到不同的构件中,
比如浏览器 HTML对应构件Applet,JSP对应构件Servlet,EJB容器对应 Bean(包括Simple Bean,SessionBean),与数据库交互有EntityBean,这样对应用程序就有较好的整体一致性的开发体验,

2 实现N层体系结构

假如我们要在golang语言中利用Gin框架的构件实现N层结构,或者借助其他web开发框架对应构件,以下为go语言中对应的构件名称

  • Applet

在现代 web 开发中,Applet 已不再常用。
一般使用前端框架如 Vue.js、React 或 Angular 来实现类似的功能。
它们与服务器通过 RESTful API 进行交互。

  • Servlet

在go语言框架中,比如Gin 的处理函数(Handler)和中间件(Middleware)可以看作是 Servlet 的对应物。它们负责处理 HTTP 请求和生成响应。

  • EJB 容器。

Go 语言中没有直接对应的 EJB 容器,但 Gin 框架及其生态系统中有很多库和工具可以实现类似的功能。
例如:

Dependency Injection (DI):可以使用依赖注入库如 google/wire 或 uber/dig。
Configuration Management:可以使用 viper 来管理配置。
  • SessionBean

在 Gin 框架中,可以使用中间件来管理会话。
常用的会话中间件包括 gin-contrib/sessions,它可以处理会话数据的存储和管理。

  • EntityBean

对应于与数据库交互的 EntityBean,在 Go 语言中,常用 ORM 库如 gorm 或 ent 可以实现类似的功能。
它们提供了与数据库进行 CRUD 操作的方便方法。

3 go 语言N层体系实例:

实现 Handler(Servlet)

package main

import (
    "github.com/gin-gonic/gin"
)

func main() {
    r := gin.Default()
    r.GET("/ping", func(c *gin.Context) {
        c.JSON(200, gin.H{
            "message": "pong",
        })
    })
    r.Run() // listen and serve on 0.0.0.0:8080
}

实现 Session Management(SessionBean)

package main

import (
    "github.com/gin-gonic/gin"
    "github.com/gin-contrib/sessions"
    "github.com/gin-contrib/sessions/cookie"
)

func main() {
    r := gin.Default()
    store := cookie.NewStore([]byte("secret"))
    r.Use(sessions.Sessions("mysession", store))

    r.GET("/session", func(c *gin.Context) {
        session := sessions.Default(c)
        var count int
        v := session.Get("count")
        if v == nil {
            count = 0
        } else {
            count = v.(int)
            count++
        }
        session.Set("count", count)
        session.Save()
        c.JSON(200, gin.H{"count": count})
    })

    r.Run(":8080")
}

4 实现数据库ORM操作实例 Database Interaction(EntityBean)

  • 使用gorm

          package main
    
          import (
              "github.com/gin-gonic/gin"
              "gorm.io/driver/sqlite"
              "gorm.io/gorm"
          )
    
          type User struct {
              ID   uint   `json:"id" gorm:"primaryKey"`
              Name string `json:"name"`
          }
    

创建ORM对象映射并操作数据

        func main() {
            db, err := gorm.Open(sqlite.Open("test.db"), &gorm.Config{})
            if err != nil {
                panic("failed to connect database")
            }

    db.AutoMigrate(&User{})

    r := gin.Default()

    r.POST("/users", func(c *gin.Context) {
        var user User
        if err := c.ShouldBindJSON(&user); err == nil {
            db.Create(&user)
            c.JSON(200, user)
        } else {
            c.JSON(400, gin.H{"error": err.Error()})
        }
    })

    r.GET("/users/:id", func(c *gin.Context) {
        var user User
        if err := db.First(&user, c.Param("id")).Error; err != nil {
            c.JSON(404, gin.H{"error": "User not found"})
        } else {
            c.JSON(200, user)
        }
    })

    r.Run(":8080")
}

5 使用ent的ORM数据操作

  • 使用 ent

结合 ent 作为 ORM 的完整示例,包括处理 HTTP 请求(Servlet)、会话管理(SessionBean)和数据库交互(EntityBean)。

项目结构

myapp/
├── main.go
├── ent/
│   ├── generate.go
│   ├── schema/
│   │   └── user.go
go.mod

首先,初始化一个新的 Go 模块并添加所需的依赖:

go mod init myapp
go get github.com/gin-gonic/gin
go get github.com/gin-contrib/sessions
go get github.com/gin-contrib/sessions/cookie
go get entgo.io/ent
go get entgo.io/ent/entc
go get entgo.io/ent/dialect/sqlite
ent/schema/user.go

创建 User 数据模型:

package schema

import (
    "entgo.io/ent"
    "entgo.io/ent/schema/field"
)

type User struct {
    ent.Schema
}

func (User) Fields() []ent.Field {
    return []ent.Field{
        field.String("name").NotEmpty(),
    }
}

func (User) Edges() []ent.Edge {
    return nil
}
ent/generate.go

生成 ent 代码:

package ent

//go:generate go run entgo.io/ent/cmd/ent generate ./schema

然后运行 go generate ./ent 生成代码。

main.go

在 main.go 中,设置 Gin 框架,配置会话管理,并实现 CRUD 操作:

package main

import (
    "context"
    "log"
    "net/http"

    "github.com/gin-contrib/sessions"
    "github.com/gin-contrib/sessions/cookie"
    "github.com/gin-gonic/gin"
    "entgo.io/ent/dialect/sqlite"
    "entgo.io/ent/dialect"
    "myapp/ent"
    _ "github.com/mattn/go-sqlite3"
)

func main() {

使用ent实例化数据库操作

    // Initialize ent client
    client, err := ent.Open(dialect.SQLite, "file:ent?mode=memory&cache=shared&_fk=1")
    if err != nil {
        log.Fatalf("failed opening connection to sqlite: %v", err)
    }
    defer client.Close()
    // Run the auto migration tool.
    if err := client.Schema.Create(context.Background()); err != nil {
        log.Fatalf("failed creating schema resources: %v", err)
    }

    r := gin.Default()

设置会话存储和数据库操作

    // Set up session store
    store := cookie.NewStore([]byte("secret"))
    r.Use(sessions.Sessions("mysession", store))

    // Routes
    r.POST("/users", func(c *gin.Context) {
        var user struct {
            Name string `json:"name"`
        }
        if err := c.ShouldBindJSON(&user); err != nil {
            c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
            return
        }

        u, err := client.User.Create().SetName(user.Name).Save(context.Background())
        if err != nil {
            c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
            return
        }
        c.JSON(http.StatusOK, u)
    })

    r.GET("/users/:id", func(c *gin.Context) {
        id := c.Param("id")
        u, err := client.User.Get(context.Background(), id)
        if err != nil {
            c.JSON(http.StatusNotFound, gin.H{"error": "User not found"})
            return
        }
        c.JSON(http.StatusOK, u)
    })

会话接口中实现了一个访问计数器,每次GET调用该接口将增加计数并保存到会话数据中。

    r.GET("/session", func(c *gin.Context) {
        session := sessions.Default(c)
        var count int
        v := session.Get("count")
        if v == nil {
            count = 0
        } else {
            count = v.(int)
            count++
        }
        session.Set("count", count)
        session.Save()
        c.JSON(http.StatusOK, gin.H{"count": count})
    })

    r.Run(":8080")
}

6 运行项目

确保所有依赖和生成的代码都已经准备好,然后运行应用:

go run main.go

现在,应用将在 http://localhost:8080 运行,可以使用 curl 或 Postman 等工具测试以下接口:

创建用户:POST /users
获取用户:GET /users/:id
会话管理:GET /session

7 小结

本文介绍了如何在Go语言中使用Gin框架实现N层体系结构,借鉴了J2EE平台的多层分布式应用程序模型。

示例中使用了gorm和ent作为ORM工具,展示了如何创建用户、获取用户信息以及管理会话数据。最后,强调了运行项目的步骤,并总结了如何在Gin框架中结合ent实现N层体系结构的基本功能。

这两个示例展示了如何在 Gin 框架中使用ORM来处理数据库交互,同时实现会话管理和基本的 HTTP 请求处理,实际的项目开发中可能需要更深入更自由地使用SQL编写复杂的查询,相比ORM工具此时使用查询生成器更具有优势。

【版权声明】本文为华为云社区用户原创内容,未经允许不得转载,如需转载请自行联系原作者进行授权。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。