Go的干净模板,领域驱动设计,清洁架构,Gin和GORM
GitHub Repo: https://github.com/resotto/goilerplate
Goilerplate
Go的干净样板,领域驱动设计,清洁架构,Gin和GORM。
什么是Goilerplate?
- 使用清洁架构的好例子。
- Go,Domain-Driven Design,Clean Architecture,Gin和GORM的火箭入门指南。
谁是Goilerplate的主要用户?
- 各种地鼠(新手到专业人士)。
为什么选择Goilerplate?
- Go中易于应用的样板。
注意
- 默认的应用程序/测试代码是微不足道的,因为您将编写很酷的逻辑。
- bitbank的公共API,即位于东京的比特币交易所,默认情况下用于某些端点。
开始
go get -u github.com/resotto/goilerplate # might take few minutes
cd ${GOPATH}/src/github.com/resotto/goilerplate
go run cmd/app/main.go # from root directory
open http://0.0.0.0:8080
Goilerplate via SSH
go get
默认情况下,通过 HTTPS 获取 GitHub 存储库。所以你可能会失败:go get
~ > go get -u github.com/resotto/goilerplate
# cd .; git clone -- https://github.com/resotto/goilerplate /Users/resotto/go/src/github.com/resotto/goilerplate
Cloning into '/Users/resotto/go/src/github.com/resotto/goilerplate'...
fatal: could not read Username for 'https://github.com': terminal prompts disabled
package github.com/resotto/goilerplate: exit status 128
如果您通过 SSH 运行 GitHub 存储库,请运行以下命令:go get
git config –global url.git@github.com:.insteadOf https://github.com/
然后,请再次尝试入门。
入口
-
使用模板
GET /
- 注意:以下路径来自CURRENT目录,因此请从根目录运行Gin。
r.LoadHTMLGlob(“internal/app/adapter/view/*”)
-
使用位库的公共 API
GET /ticker
GET /candlestick
- 注意:由于其API限制,这适用于0AM ~ 3PM(UTC)。
-
使用PostgreSQL
- 注意:请先运行此步骤的 postgres 容器。
GET /parameter
GET /order
包结构
├── LICENSE
├── README.md
├── build # Packaging and Continuous Integration
│ ├── Dockerfile
│ └── init.sql
├── cmd # Main Application
│ └── app
│ └── main.go
├── internal # Private Codes
│ └── app
│ ├── adapter
│ │ ├── controller.go # Controller
│ │ ├── postgresql # Database
│ │ │ ├── conn.go
│ │ │ └── model # Database Model
│ │ │ ├── card.go
│ │ │ ├── cardBrand.go
│ │ │ ├── order.go
│ │ │ ├── parameter.go
│ │ │ ├── payment.go
│ │ │ └── person.go
│ │ ├── repository # Repository Implementation
│ │ │ ├── order.go
│ │ │ └── parameter.go
│ │ ├── service # Application Service Implementation
│ │ │ └── bitbank.go
│ │ └── view # Templates
│ │ └── index.tmpl
│ ├── application
│ │ ├── service # Application Service Interface
│ │ │ └── exchange.go
│ │ └── usecase # Usecase
│ │ ├── addNewCardAndEatCheese.go
│ │ ├── ohlc.go
│ │ ├── parameter.go
│ │ ├── ticker.go
│ │ └── ticker_test.go
│ └── domain
│ ├── factory # Factory
│ │ └── order.go
│ ├── order.go # Entity
│ ├── parameter.go
│ ├── parameter_test.go
│ ├── person.go
│ ├── repository # Repository Interface
│ │ ├── order.go
│ │ └── parameter.go
│ └── valueobject # ValueObject
│ ├── candlestick.go
│ ├── card.go
│ ├── cardbrand.go
│ ├── pair.go
│ ├── payment.go
│ ├── ticker.go
│ └── timeunit.go
└── testdata # Test Data
└── exchange_mock.go
领域层
- 清洁架构的核心。它说"实体"。
应用层
- 来自核心的第二层。它说"用例"。
适配器层
- 第三层来自核心。它说"控制器/网关/演示者"。
外层
- 第四层从核心。它说"设备/ DB / 外部接口/ UI / Web"。
-
我们在这个层中不写太多代码。
-
如何跨越这些图层的边界
在 Clean Architecture 中,有依赖关系规则:
此规则表示源代码依赖项只能指向内部。在一个内圈里,没有任何东西可以知道关于外圈里某件事的任何事情。
换句话说,依赖注入需要遵循此规则。
因此,请遵循以下四个步骤:
- 定义接口
- 以参数为接口并调用其函数
- 实施它
- 注入依赖关系
在这里,我选取存储库的示例。
存储 库
.
└── internal
└── app
├── adapter
│ ├── controller.go # 4\. Dependency Injection
│ └── repository
│ └── parameter.go # 3\. Implementation
├── application
│ └── usecase
│ └── parameter.go # 2\. Interface Function Call
└── domain
├── parameter.go
└── repository
└── parameter.go # 1\. Interface
- 域层接口:
package repository
import "github.com/resotto/goilerplate/internal/app/domain"
// IParameter is interface of parameter repository
type IParameter interface {
Get() domain.Parameter
}
- 应用程序层用例:
package usecase
// NOTICE: This usecase DON'T depend on Adapter layer
import (
"github.com/resotto/goilerplate/internal/app/domain"
"github.com/resotto/goilerplate/internal/app/domain/repository"
)
// Parameter is the usecase of getting parameter
func Parameter(r repository.IParameter) domain.Parameter {
return r.Get()
}
- 适配器层的实现:
package repository
// Parameter is the repository of domain.Parameter
type Parameter struct{}
// Get gets parameter
func (r Parameter) Get() domain.Parameter {
db := postgresql.Connection()
var param model.Parameter
result := db.First(¶m, 1)
if result.Error != nil {
panic(result.Error)
}
return domain.Parameter{
Funds: param.Funds,
Btc: param.Btc,
}
}
- 适配器层控制器处的依赖关系注入:
package adapter
// NOTICE: Controller depends on INNER CIRCLE so it points inward (The Dependency Rule)
import (
"github.com/gin-gonic/gin"
"github.com/resotto/goilerplate/internal/app/adapter/repository"
"github.com/resotto/goilerplate/internal/app/application/usecase"
)
var (
parameterRepository = repository.Parameter{}
)
func (ctrl Controller) parameter(c *gin.Context) {
parameter := usecase.Parameter(parameterRepository) // Dependency Injection
c.JSON(200, parameter)
}
应用程序服务的实现也是相同的。
依赖注入
在 Goilerplate 中,依赖项是手动注入的。
- 注意:如果Go中的其他DI工具没有成为某种应用程序框架,它也是可以接受的。
有两种传递依赖项的方法:
- 使用位置参数
- 使用关键字参数
使用位置参数
首先,使用接口类型的参数定义用例。
package usecase
func Parameter(r repository.IParameter) domain.Parameter { // Take Argument as Interface
return r.Get()
}
其次,初始化实现并将其提供给用例。
package adapter
var (
parameterRepository = repository.Parameter{} // Initialize Implementation
)
func (ctrl Controller) parameter(c *gin.Context) {
parameter := usecase.Parameter(parameterRepository) // Inject Implementation to Usecase
c.JSON(200, parameter)
}
使用关键字参数
首先,定义参数结构和用例。
package usecase
// OhlcArgs are arguments of Ohlc usecase
type OhlcArgs struct {
E service.IExchange // Interface
P valueobject.Pair
T valueobject.Timeunit
}
func Ohlc(a OhlcArgs) []valueobject.CandleStick { // Take Argument as OhlcArgs
return a.E.Ohlc(a.P, a.T)
}
然后,使用关键字参数初始化结构并将其提供给用例。
package adapter
var (
bitbank = service.Bitbank{} // Implementation
)
func (ctrl Controller) candlestick(c *gin.Context) {
args := usecase.OhlcArgs{ // Initialize Struct with Keyword Arguments
E: bitbank, // Passing the implementation
P: valueobject.BtcJpy,
T: valueobject.OneMin,
}
candlestick := usecase.Ohlc(args) // Give Arguments to Usecase
c.JSON(200, candlestick)
}
全局喷油器变量
在手动DI中,实现初始化成本将很昂贵。
因此,让我们使用全局注入器变量,以便仅初始化它们一次。
package adapter
var (
bitbank = service.Bitbank{} // Injecter Variable
parameterRepository = repository.Parameter{}
orderRepository = repository.Order{}
)
func (ctrl Controller) ticker(c *gin.Context) {
pair := valueobject.BtcJpy
ticker := usecase.Ticker(bitbank, pair) // DI by passing bitbank
c.JSON(200, ticker)
}
如何从Goilerplate开始
使用Goilerplate,您可以顺利地开始您的项目。
为了便于解释,让我们使用 Goilerplate 创建以下规范的 CRUD 的简单"CR"部分。
规格:
- 有三个实体,如"客户"、“产品和订单”。
- 订单聚合客户和产品(订单是聚合根)。
- 只有一个用例用于创建订单。
通知:
- 为方便起见,此处显示了最小代码。
- 为方便起见,此说明中没有测试代码。
首先,请准备.go文件,并遵循以下软件包布局。
包装布局
└── internal
└── app
├── adapter
│ ├── controller.go # Controller
│ └── repository # Repository Implementation
│ ├── customer.go
│ ├── product.go
│ └── order.go
├── application
│ └── usecase # Usecase
│ └── createOrder.go
└── domain
├── customer.go # Entity
├── product.go # Entity
├── order.go # Entity
└── repository # Repository Interface
├── customer.go
├── product.go
└── order.go
定义实体
其次,让我们创建实体、客户、产品和订单。
|
|
定义存储库接口
定义实体后,让我们在包中准备它们的存储库。domain
|
|
定义用例
然后,让我们准备创建顺序的用例。
|
|
定义存储库实现
准备用例后,让我们在包中实现存储库接口。adapter
但是,为方便起见,此处省略了这一部分。
// order.go
package repository
import (
"domain" // simplified for convenience
)
type Order struct{}
func (o Order) Save(order domain.Order) {
// omitted here for convenience
}
定义控制器
最后,让我们定义控制器来调用创建订单的用例。
|
|
就是这样!
测试
|
|
有两条规则:
- 包含测试代码的包的名称为 。
xxx_test
- 在包装上放置模拟。
testdata
测试包结构
|
|
实体
请在实体所在的同一目录中编写测试。
|
|
用例
请在包上准备模拟(如果需要),并在与用例相同的目录中编写测试。testdata
|
|
命名约定
接口
- 添加前缀,如 。
I``````IExchange
- 注意:如果您可以区分接口和实现,则任何命名约定都是可以接受的。
模拟
- 添加前缀,如 。
M``````MExchange
- 注意:如果您可以区分模拟和生产,则任何命名约定都是可以接受的。
文件
- 文件名可以重复。
- 对于测试,请添加后缀,如 .
_test``````parameter_test.go
- 对于模拟,请添加后缀,如 .
_mock``````exchange_mock.go
包
与GOCHK
Gochk,用于go文件的静态依赖关系分析工具,赋予Goilerplate如此多的功能!
让我们将 Gochk 合并到 CI 流程中。
|
|
然后,它的结果是:
使用PostgreSQL
首先,从 GitHub 容器注册表拉取 docker 映像,并使用以下命令运行容器:ghcr.io/resotto/goilerplate-pg
docker run -d -it –name pg -p 5432:5432 -e POSTGRES_PASSWORD=postgres ghcr.io/resotto/goilerplate-pg:latest
然后,让我们来看看:
open http://0.0.0.0:8080/parameter
open http://0.0.0.0:8080/order
建筑形象
如果从 GitHub 容器注册表拉取映像失败,还可以从 Dockerfile 生成 Docker 映像。
cd build
docker build -t goilerplate-pg:latest .
docker run -d -it –name pg -p 5432:5432 -e POSTGRES_PASSWORD=postgres goilerplate-pg:latest
Docker 映像
从 GitHub 容器注册表中提取的映像是从简单的 Dockerfile 和 init.sql构建的。
|
|
|
|
- 原文作者:知识铺
- 原文链接:https://geek.zshipu.com/post/go/Go%E7%9A%84%E5%B9%B2%E5%87%80%E6%A0%B7%E6%9D%BF%E9%A2%86%E5%9F%9F%E9%A9%B1%E5%8A%A8%E8%AE%BE%E8%AE%A1%E6%B8%85%E6%B4%81%E6%9E%B6%E6%9E%84Gin%E5%92%8CGORM/
- 版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议进行许可,非商业转载请注明出处(作者,原文链接),商业转载请联系作者获得授权。
- 免责声明:本页面内容均来源于站内编辑发布,部分信息来源互联网,并不意味着本站赞同其观点或者证实其内容的真实性,如涉及版权等问题,请立即联系客服进行更改或删除,保证您的合法权益。转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。也可以邮件至 sblig@126.com