🥈

GinとSQLiteでアプリケーションを作ってみる

2024/01/03に公開

Ginのチュートリアルをやってみた!

普段は、DartやってるのですけどGo言語を使う機会が今まであったので、Udemyでチュートリアルをやった後にクリーンアーキテクチャーを使って作ったアプリをレイヤー分けするのもやってみて、簡単だけど難しいアプリを作ってみた。

  1. Goの環境構築をする
go mod init shop
  1. Ginを導入する
go get -u github.com/gin-gonic/gin
  1. main.goを作成してHello Worldしてみる

curlコマンドを使ってhttp://localhost:8080/でサーバーからデータをGETできるが、Thunder ClientやPostmanを使うと簡単にローカルサーバーから、HTTP GETできる。

package main

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

func main() {
    r := gin.Default()
    r.GET("/", func(c *gin.Context) {
        c.String(200, "Hello World")
    })
    r.Run() // listen and serve on 0.0.0.0:8080
}
  1. sqlite3を導入する
go get github.com/mattn/go-sqlite3
  1. gormを導入する
go get -u gorm.io/gorm
go get -u gorm.io/driver/sqlite

go mod tidyを実行して依存関係を追加する:

go mod tidy

CRUDと特定のデータを検索することができるアプリ:

package main

import (
	"net/http"

	"github.com/gin-gonic/gin"
	"github.com/jinzhu/gorm"
	_ "github.com/jinzhu/gorm/dialects/sqlite"
)

type Shop struct {
	gorm.Model
	Title       string
	Description string
}

func main() {
	db, err := gorm.Open("sqlite3", "shop.db")
	if err != nil {
		panic("failed to connect database")
	}

	// Migrate the schema
	db.AutoMigrate(&Shop{})

	r := gin.Default()

	// Create
	r.POST("/shops", func(c *gin.Context) {
		var shop Shop
		if err := c.ShouldBindJSON(&shop); err != nil {
			c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
			return
		}

		db.Create(&shop)
		c.JSON(http.StatusOK, shop)
	})

	// Read
	r.GET("/shops/:id", func(c *gin.Context) {
		var shop Shop
		if err := db.First(&shop, c.Param("id")).Error; err != nil {
			c.JSON(http.StatusBadRequest, gin.H{"error": "Record not found!"})
			return
		}

		c.JSON(http.StatusOK, shop)
	})

	// Read all
	r.GET("/shops", func(c *gin.Context) {
		var shops []Shop
		if err := db.Find(&shops).Error; err != nil {
			c.JSON(http.StatusBadRequest, gin.H{"error": "Record not found!"})
			return
		} else {
			db.Find(&shops)
			c.JSON(http.StatusOK, shops)
		}
	})

	// Update
	r.PUT("/shops/:id", func(c *gin.Context) {
		var shop Shop
		if err := db.First(&shop, c.Param("id")).Error; err != nil {
			c.JSON(http.StatusBadRequest, gin.H{"error": "Record not found!"})
			return
		}

		if err := c.ShouldBindJSON(&shop); err != nil {
			c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
			return
		}

		db.Save(&shop)
		c.JSON(http.StatusOK, shop)
	})

	// Delete
	r.DELETE("/shops/:id", func(c *gin.Context) {
		var shop Shop
		if err := db.First(&shop, c.Param("id")).Error; err != nil {
			c.JSON(http.StatusBadRequest, gin.H{"error": "Record not found!"})
			return
		}

		db.Delete(&shop)
		c.JSON(http.StatusOK, gin.H{"success": "Record has been deleted!"})
	})

	r.Run()
}

使う時は、こんな感じですね。curlだとやりにくいので、GUIツール使ってます。指定したローカルホストのURLにアクセスすれば、データの追加、取得、更新、削除、検索ができます。

レイヤーを分ける

同じファイルにコードを書いているとコードの記述量が増えるのと、保守がしづらい!
クリーンアーキテクチャで設計をしてみた。

これが完成品です。別のブランチにpushしてあります。
https://github.com/sakurakotubaki/GinTutorial/tree/dev-clean

.
├── README.md
├── controller
│   └── shop_controller.go
├── go.mod
├── go.sum
├── main.go
├── model
│   └── shop.go
├── repository
│   └── shop_repository.go
├── shop.db
└── usecase
    └── shop_usecase.go

このプロジェクトは、クリーンアーキテクチャの原則に基づいてレイヤー分けされています。各ディレクトリの役割は以下の通りです:

controller:

このディレクトリには、HTTPリクエストを受け取り、適切なユースケースを呼び出し、その結果をHTTPレスポンスとして返すコントローラが含まれています。

model:

このディレクトリには、アプリケーションのビジネスロジックを表すデータモデルが含まれています。これらのモデルは、データベースのテーブルを表現するために使用されます。

repository:

このディレクトリには、データベースとのやり取りを抽象化するリポジトリインターフェースとその実装が含まれています。これにより、ビジネスロジックはデータベースの具体的な実装から分離されます。

usecase:

このディレクトリには、アプリケーションのビジネスロジックを表すユースケースが含まれています。ユースケースは、リポジトリを通じてデータベースとやり取りを行います。

main.go:

このファイルはアプリケーションのエントリーポイントで、全ての依存関係をワイヤリングし、サーバーを起動します。

shop.db:

これはアプリケーションのデータベースファイルです。

go.modとgo.sum:

これらのファイルはGoの依存関係管理に使用されます。go.modはプロジェクトの依存関係を宣言し、go.sumは各依存関係の期待されるコンテンツを提供します。

感想

GoはJavaよりは簡単そうだと思いました。nilとかクラスがないとか、慣れない部分がありますが、パターンさえ覚えればCRUDするアプリぐらいは作れそうと思いました。
今回は、初心者が作ったクリーンアーキテクチャーのDB使ったアプリなので、改良は必要かもしれないです。

Jboy王国メディア

Discussion