新手项目组织的疑惑

2022-05-30 09:54:26 +08:00
 dzdh

新手,用一个月左右看了看语法 demo 啥的各种小 demo 看了看觉得自己行了。

然后想写个项目( ssh 跳板机)练练手,在项目组织上又迷糊了,现在这么组织的

├── go.mod
├── go.sum
├── internal
│   ├── admin
│   │   ├── resources
│   │   │   └── assets
│   │   │       └── FS.go  < FS=embed *.js *.css **/*.js **/*.css
│   │   └── server.go
│   ├── bootstrap.go
│   ├── config
│   │   └── config.go
│   ├── model
│   │   ├── init.go
│   │   └── models.go
│   └── ssh
│       ├── clients.go
│       ├── error.go
│       ├── forward.go
│       ├── handler.go
│       └── server.go
├── main.go

想实现 在 main.go里 调用 bootstrap.setupadmin() bootstrap.setupsshserver() 同时也是为了方便的以后能把 admin 和 sshserver 分离开。

那我在什么时候初始化数据库连接呢?放在 model.init 里? init 方法的话,我在哪传入配置信息呢?(数据库地址啊、密码啊啥的)。现在是 model.init.go 里是 Initialize(cfg *internal.Config.Global)

所以完整的 main.go 是这样的

func main() {
    cfgfilepath = // parseflag
    
    if cfg, err = ioutil.ReadAll(cfgfilepath) err!=nil {panic
    config.Global.LoadConfig(cfg)
    
    model.Initialize(config.Global)
    
    // 支持信号退出用
    context = context.withcancel(...
    
    // 这两个里是 go func..
    model.SetupAdmin(config.Global, context)
    model.SetupSSHServer(config.Global, context)
    
    <- 等待信号
}

这样组织代码有问题吗?怎么做能让 admin 和 sshserver 两个 net.Listen 任意一个有问题就整个退出呢

3560 次点击
所在节点    Go 编程语言
25 条回复
hteen
2022-05-30 10:00:02 +08:00
iseki
2022-05-30 10:04:30 +08:00
我喜欢在外卖把数据库连接初始化好,然后丢进 model 包里。这样将来如果测试时需要对数据库对象 mock ,可以很容易做到
wheeler
2022-05-30 10:05:50 +08:00
数据库初始化放 main 不能吗? init 塞数据库初始化是坏实践。
wheeler
2022-05-30 10:08:13 +08:00
“怎么做能让 admin 和 sshserver 两个 net.Listen 任意一个有问题就整个退出呢”

用 channel 不能吗?
dzdh
2022-05-30 10:09:34 +08:00
@wheeler #4
所以我弄了个共用的 context, cancel 但是总觉得有啥问题
dzdh
2022-05-30 10:10:11 +08:00
@iseki 有啥小 demo 可以瞻仰一下的嘛
saltbo
2022-05-30 10:12:58 +08:00
/internal/app/ssh
/internal/app/admin
/internal/pkg/config
/internal/pkg/model
/internal/pkg/dao

1. 不要在 model 里做数据库操作,数据库操作应该放到 dao 里。model 里只做赋值及数据结构转换之类的操作。在 model 里做数据库操作,盲猜 php 的习惯。

2. config 你都 Global 了,还传啥传,直接在对应的包里使用就好了

3. dao.init 放到 main 里也行。还有人喜欢放 init 里,但我个人比较反感这种,因为不是显式调用,很容易忽略。更好的方式是封装个 Engine 或者 App 之类的 struct ,然后再 main 里调用它,这样 main 里就比较干净
securityCoding
2022-05-30 10:24:09 +08:00
@wheeler init 做外部资源类初始化就是埋雷,哪天下游炸了就有意思了。
wheeler
2022-05-30 10:29:19 +08:00
@dzdh 不好意思,没明白。能贴完整代码吗?
weiwenhao
2022-05-30 10:43:59 +08:00
github.com/spf13/cobra // 做启动入口
github.com/spf13/viper // 做配置
github.com/robfig/cron // 做定时任务
github.com/gin-gonic/gin // 做 http 服务的

mysql/redis/es 等等数据库都是用连接池的方式初始化,也就是入口处初始化一次就好啦。确实不推荐用原生的 init, 自己再创建 Init ,需要的时候手动调用就好了。不然你 ./test version 就调用了一堆 init 方法。

相关结构推荐扁平化,比如你这个 internal 感觉没啥必要呀,你这就一个编译入口 main 直接把 admin,config,model,ssh 摆出来不就好了。
sophos
2022-05-30 10:53:26 +08:00
这个可以参考一下: https://github.com/douyu/jupiter-layout
lxz6597863
2022-05-30 10:59:25 +08:00
任意一个有问题就整个退出 golang.org/x/sync/errgroup
sciel
2022-05-30 11:20:32 +08:00
goframe 工程目录结构可以参考一下 https://goframe.org/pages/viewpage.action?pageId=30740166
Gota
2022-05-30 12:00:24 +08:00
我之前写过一篇和微服务组织相关的博客,也有配套的 DEMO 代码,可以参考看看: https://blog.igota.net/posts/20220422/
dzdh
2022-05-30 12:36:13 +08:00
@lxz6597863 #12 支持主动退出吗?比如 ctrl-c
Macolor21
2022-05-30 13:21:04 +08:00
搞来搞去,又变成了 Java 那样的工程化分层。
这种帖子下,又没人吐槽这种分层了。
zackkson1991
2022-05-30 13:38:59 +08:00
我实践告诉我, "Java 那套"DAO ,Service ,Controller 的分层还是很清晰的,而且拓展性还是很不错的。虽然我的项目不大。我觉得很多人说这是属于“Java 那套分层”。但是我认为不是输入 java 的, 而是属于大多数软件的工程项目的。
然后, 我想不到有比这个更好的文件结构了。
yuancoder
2022-05-30 13:59:10 +08:00
我是根据模块功能划分目录,然后在 main 方法里依次调用模块相关的初始化方法,全局的 config ,logger 之类的都是直接使用的
dzdh
2022-05-30 14:36:51 +08:00
@wheeler #9

bootstrap.setupadmin(config, context, cacnel) 是

func setup..
srv := ssh.NewServer()
go func { select { <-context.Done: srv.shutdown
srv.ListenAndServe()

这样总感觉有点问题,感觉不伦不类的
dzdh
2022-05-30 14:38:02 +08:00
@wheeler #3

放到 main? 现在就是
在 main 里
model.Initialize(..) { gorm.Open..

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/856138

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX