V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
The Go Programming Language
http://golang.org/
Go Playground
Go Projects
Revel Web Framework
gitrebase
V2EX  ›  Go 编程语言

关于 Go Web 项目的项目结构

  •  3
     
  •   gitrebase · 2023-07-25 10:00:15 +08:00 · 5838 次点击
    这是一个创建于 479 天前的主题,其中的信息可能已经有所发展或是发生改变。

    想搞 DDD ,但自觉无能,就借鉴了一下 DDD 的分层思想,搞出了如下的结构

    ├── adaptor
    │   ├── mq
    │   ├── rpc
    │   ├── task // 定时任务
    │   └── web
    ├── config
    ├── handler
    │   └── user.go
    ├── infrastructure
    │   ├── cache
    │   │   └── user.go
    │   ├── message
    │   ├── mp
    │   ├── remote
    │   └── repository
    │       └── user.go
    ├── main.go
    ├── manager
    │   └── user.go
    ├── model
    │   ├── bo
    │   ├── dto
    │   ├── po
    │   └── vo
    └── service
        ├── event // 事件总线(事件模型)
        └── user.go
    

    但还是感觉不是很对?

    比如 service 下,如果 user.go 、post.go 等不同模块的都放在一个 service 包下,那这个 service 包的内聚性堪忧;如果 service 下各建子包,那命名上感觉又有点奇怪(比如路径是 service/user ,但是 package 如果是 user 就可能和 handler 、repository 等包下面的 user 重复了,如果是 userservice 又感觉看着好怪?)

    想问问万能的 V 友们有什么指点或建议吗

    52 条回复    2023-07-26 10:07:55 +08:00
    motecshine
        1
    motecshine  
       2023-07-25 10:05:23 +08:00   ❤️ 3
    简单易用的语言被搞出 花来了,这么多文件夹,还有什么 dto bo ,根本不知道在干啥,复杂的事情做简单是件很难得事情
    gitrebase
        2
    gitrebase  
    OP
       2023-07-25 10:07:17 +08:00
    @motecshine 感谢您的回复!我个人对 dto bo 那些也不是很了解,请问如果不设置这些的话应该怎么搞呀
    lasuar
        3
    lasuar  
       2023-07-25 10:09:43 +08:00
    楼上+1 ,成熟的 gopher 有合理并简洁的项目目录结构规划,而不是照搬 java 那套。对于 ddd ,楼主也是了解个半桶水,ddd 压根没有所谓明确的分层结构,各种网文 ddd 架构都是玩家 yy 的,ddd 如同太极一样,只重其意不重其招,建议专门找两本 ddd 书看看,拒绝网文,从你我做起。
    gitrebase
        4
    gitrebase  
    OP
       2023-07-25 10:13:02 +08:00
    @lasuar 唔,可能我描述的不是很清,我不是想搞 DDD ,就是想设计一下一个 Go Web 项目的目录结构,不过感谢您的建议,今年就找两本专业的书看看
    swulling
        5
    swulling  
       2023-07-25 10:15:12 +08:00 via iPhone
    contradictspiral
        6
    contradictspiral  
       2023-07-25 10:18:33 +08:00
    可以参考下 GoFrame 的,感觉对单体 go web 应用来说设计的挺好,文档中也有很多作者的思考
    lasuar
        7
    lasuar  
       2023-07-25 10:19:15 +08:00   ❤️ 2
    https://github.com/golang-standards/project-layout

    如果一定要找个标准模仿,可以看看找个 4w 星的 repo 。

    >多说几句,在任何时候都照搬某个标准也是没啥意义的。要掌握每个目录的意义, 并在确实需要的时候才规划,并且根据各种因素进行迭代变更;根据项目进展而演进项目架构,从无到有,从有到无,从单一到多个,从多个到合并为一个,从分散到聚合,从高度聚合到再度分离。。。这样才能在项目开发的各个阶段最大化开发效率,并且不会影响开发人员对项目布局的架构认知(考验项目主程的水平)。
    weiwenhao
        8
    weiwenhao  
       2023-07-25 10:19:22 +08:00   ❤️ 7
    ❯ tree .
    .
    ├── cmd
    │   └── root.go
    ├── controller
    │   └── user
    │   └── user.go
    ├── go.mod
    ├── go.sum
    ├── main.go
    ├── middleware
    ├── model
    │   ├── model.go
    │   └── user.go
    ├── router
    │   └── v1.go
    ├── service
    └── util
    └── helper.go

    平面架构了解一下, 总之就是在项目根目录下加主要模块, 比如定时任务,就加 jobs. 需要什么就加什么模块. 另外 model 的核心库是 gorm, router 的核心库是 gin, cmd 目录的核心库是 spf13/cobra
    NX2023
        9
    NX2023  
       2023-07-25 10:22:59 +08:00
    @weiwenhao 和佬的想法一致😀
    gitrebase
        10
    gitrebase  
    OP
       2023-07-25 10:24:48 +08:00
    @lasuar 感谢佬的回复,受益匪浅
    gitrebase
        11
    gitrebase  
    OP
       2023-07-25 10:26:10 +08:00
    @weiwenhao 佬,扁平化的话,每个包的内聚性是不是不太好呀,比如 user.go 里的私有内容就能被 post.go 访问到了
    6IbA2bj5ip3tK49j
        12
    6IbA2bj5ip3tK49j  
       2023-07-25 10:28:57 +08:00   ❤️ 9
    去写 java 吧,求求你了。
    wkong
        13
    wkong  
       2023-07-25 10:29:20 +08:00
    开源项目
    https://github.com/TangSengDaoDao/TangSengDaoDaoServer

    项目结构:

    ├── assets
    | ├── assets // 项目里用到的一些资源文件,比如系统账号头像,文件助手头像,系统群头像等等
    | ├── resources // 文件服务需要初始化的一些资源文件 比如用户默认头像。
    | ├── sql // 项目里的所有 sql 脚本
    | └── web // 系统用到的 web 的一些 html 文件
    ├── configs // 项目里的所有配置
    | ├── push // 苹果推送的证书放置目录
    | └── tsdd.yaml // 项目的配置文件
    ├── docker // docker 相关的脚本
    ├── docs // 项目文档/截图
    ├── internal
    | ├── api ==================== 项目所有对外的 API(业务代码基本都在这个目录下) ====================
    | | ├── api.go // api 的所有模块的入口文件
    | | ├── base // 基础模块
    | | ├── channel // 频道模块
    | | ├── common // 常用模块
    | | ├── file // 文件服务模块
    | | ├── group // 群组模块
    | | ├── message // 消息模块
    | | ├── qrcode // 二维码模块
    | | ├── report // 举报模块
    | | ├── robot // 机器人模块
    | | ├── source // 来源模块
    | | ├── statistics // 统计模块
    | | ├── user // 用户模块
    | | └── webhook // webhook 模块
    | ================================================================================
    | ├── config // 贯穿项目全局的上下文对象和配置对象
    | ├── server // 服务启动
    | | └── server.go
    | └── testutil // 单元测试工具类
    | └── test.go
    ├── pkg // 项目里的一些公共的包
    ├── testenv // 项目基础设施环境
    | └── docker-compose.yaml
    └── main.go // 项目入库 main.go 文件
    NX2023
        14
    NX2023  
       2023-07-25 10:30:55 +08:00
    @gitrebase #11 这可以分模块化,大平层是简单项目最好,要分模块的话可以每个 module 有自己的 router model controller
    me1onsoda
        15
    me1onsoda  
       2023-07-25 10:33:18 +08:00
    一眼 Javaer
    gitrebase
        16
    gitrebase  
    OP
       2023-07-25 10:35:19 +08:00
    @xgfan 佬,如果在 Go 里,有什么项目结构上的建议吗
    weiwenhao
        17
    weiwenhao  
       2023-07-25 10:36:35 +08:00
    @gitrebase

    1. controller 排版有问题, 应该是 controller/user/user.go . 也就是一个模块一个 package

    2. model 下面全部都放在 package model 下面. 是为了方便处理模型之间的关联关系, 比如 user 的多个 posts . 相反的 post 属于 user 也可以在 post 模型定义, 不会出现引用循环. 我自己是这么用的,写起来比较简单快捷.

    ```go
    type struct User {
    Posts []*Post `gorm:"-:migration" json:"posts,omitempty"`

    }

    // user 相关的方法就用接收器来写, 需要对外访问的就大写, 不需要对外访问的就小写.

    func (u *User) xxx() {
    }

    ```

    3. 另外访问的层级关系需要严格限定,避免造成引用的循环. router -> controller -> service -> model 或者 router ->controller -> model

    util 模块则可以在任意模块被访问的,和业务无关的通用模块

    4. 不是很熟悉面向对象的内聚之类的, 我的原则是写起来和看起来简单一点就行.
    1055619878
        18
    1055619878  
       2023-07-25 10:37:24 +08:00
    可以参考下 go-zero
    gitrebase
        19
    gitrebase  
    OP
       2023-07-25 10:37:39 +08:00
    @me1onsoda 不否认写过 Java ,但是现在要写 Go 了,想主动寻求改变,但对 Go 的风格不是很了解,请问佬有什么项目结构上的建议与指点吗
    crysislinux
        20
    crysislinux  
       2023-07-25 10:37:49 +08:00 via Android
    别的不说,你这个 infra 底下 repository 不是很合理呀,按我的理解 infra 底下都是放一些类似 driver 的东西,比如数据库的 client ,Redis 的 client ,rpc 库。repository interface 属于 domain 里的,然后在 service 里实现。
    简单项目大可不必搞这么复杂。不过直接平铺老实说心里确实也没啥底。
    sunhuawei
        21
    sunhuawei  
       2023-07-25 10:38:52 +08:00   ❤️ 2
    贴一个我喜欢的 Go 项目的结构,作者也曾在 v 站发过贴,我的项目也都用的这种结构
    https://github.com/answerdev/answer
    tcpdump
        22
    tcpdump  
       2023-07-25 10:39:22 +08:00   ❤️ 1
    Java 各种 *AO ,GO 各种 func

    还是 Python 看得舒服
    xuelu520
        23
    xuelu520  
       2023-07-25 10:39:31 +08:00
    什么语言,项目结构的归属都是 JAVA 向的。所以直接抄 java 吧,免得后面还得改
    loading
        24
    loading  
       2023-07-25 10:42:01 +08:00
    https://github.com/flipped-aurora/gin-vue-admin
    感觉挺合理的,前后端都有。
    gitrebase
        25
    gitrebase  
    OP
       2023-07-25 10:42:50 +08:00
    @weiwenhao 感谢佬的解答!此外还有点疑惑

    1. 关于 [controller 排版有问题, 应该是 controller/user/user.go . 也就是一个模块一个 package] ,想问下该怎么设置 controller/user 的 package name 呢,如果就默认为 user ,感觉有可能会和 service/user 等其他的同名模块冲突?

    2. 关于 [访问的层级关系需要严格限定] ,会不会出现 service 互调导致循环引用的场景呢?之前写 Java 的使用用的是 Manager 下沉,service 调度 manager ,在 Go 里有什么方案吗
    gitrebase
        26
    gitrebase  
    OP
       2023-07-25 10:45:34 +08:00
    @crysislinux 我之前也没用过这样的结构,但是看网上有些类似这样的图( https://image-static.segmentfault.com/219/409/2194092392-357c41e63032335e ),把 db 归到了 infra 中
    gitrebase
        27
    gitrebase  
    OP
       2023-07-25 10:47:51 +08:00
    @sunhuawei 感谢推荐,这就学习学习
    weiwenhao
        28
    weiwenhao  
       2023-07-25 10:50:01 +08:00   ❤️ 1
    @gitrebase

    1. 就叫 package user, 如果冲突就 import userservice "service/user" 就可以了. 没啥不雅的吧
    2. 假如 service 互相引用就说明存在共同逻辑呗, 如果是和 model 相关的就下沉到 model, 如果是通用的业务逻辑就下沉到 util, 如果都不是, 我就继续在 service 单独起一个目录来建. 实际编写过程中, 我一般不会直接创建 service, 需要复杂逻辑时才会提取到 service 里面. 否则基本都在 model 中写了.

    3. 这个不是 golang 的方案, 就是我自己的总结学习的方案. 原则上遵循瘦 controller, 胖 model, 如果 model 太胖了,才会提取一个 service package.
    gitrebase
        29
    gitrebase  
    OP
       2023-07-25 10:54:47 +08:00
    @weiwenhao 感谢佬的回复!收益颇丰!
    xz410236056
        30
    xz410236056  
       2023-07-25 10:55:32 +08:00
    等你构建好完美的文件夹时,我功能都写完了
    pkoukk
        31
    pkoukk  
       2023-07-25 10:56:59 +08:00
    https://github.com/danceyoung/paper-code/blob/master/package-style-guideline/packagestyleguideline.md
    https://github.com/danceyoung/paper-code/blob/master/package-oriented-design/packageorienteddesign.md

    我的建议是不要追求什么 best practice ,结合良好的规范和个人需求,制定适合自己的架构就好。
    抄一个架构下来,里面一大堆文件夹是空的,有什么意义呢?或者为了一行代码套了三层文件夹,引用维护起来蛋疼的要死,何必呢?
    crysislinux
        32
    crysislinux  
       2023-07-25 10:59:59 +08:00 via Android
    @gitrebase DB 确实是 infra ,但是那是指 DB 本身。repository 是具体的仓库了,它不一定从狭义的 DB 获取数据
    gitrebase
        33
    gitrebase  
    OP
       2023-07-25 11:04:53 +08:00
    @crysislinux 理解了,谢谢佬!
    hijoker
        34
    hijoker  
       2023-07-25 12:21:35 +08:00
    很多 Go 的开源项目的结构可以参考下。比如 k8s 相关的,我也赞成不要照搬目录结构
    imMetaverse
        35
    imMetaverse  
       2023-07-25 12:34:37 +08:00
    什么时候踩 Java 又成为一种时尚了 😭
    wxlwsy
        36
    wxlwsy  
       2023-07-25 12:43:46 +08:00
    java 本来好好的直到遇到了 j2ee, 看来 go 也逃不了这种命运...
    amon
        37
    amon  
       2023-07-25 12:46:25 +08:00
    这种没有标准的规范,每个公司和团队场景不同、风格也不同。
    参考开源项目是一个好办法。
    然后在实际使用的过程中,遇到划分不合理的情况再作优化。
    SimbaPeng
        38
    SimbaPeng  
       2023-07-25 13:11:16 +08:00
    Cola98
        39
    Cola98  
       2023-07-25 13:11:29 +08:00
    之前写的一个帖子,可以参考下:
    https://v2ex.com/t/865330#reply27
    cexll
        40
    cexll  
       2023-07-25 13:11:30 +08:00
    SimbaPeng
        41
    SimbaPeng  
       2023-07-25 13:18:33 +08:00
    Oktfolio
        42
    Oktfolio  
       2023-07-25 13:33:26 +08:00
    踩 Java 的果然又来了
    Desdemor
        43
    Desdemor  
       2023-07-25 13:54:59 +08:00
    贴个老大的项目给你参考下: https://github.com/8treenet/freedom
    ZGame
        44
    ZGame  
       2023-07-25 14:22:54 +08:00
    为了 d 而 d 没有意义吧... 而是要关注你的业务复杂度和前端可视化程度去做分类。不然一板一眼要累死
    zuiwu
        45
    zuiwu  
       2023-07-25 15:16:47 +08:00   ❤️ 1
    https://github.com/go-nunu/nunu
    我用这个,感觉还不错,能满足自己开发需求即可
    cosmtrek
        46
    cosmtrek  
       2023-07-25 15:41:34 +08:00
    注意分层和依赖控制,想怎么设计都行😂
    darksword21
        47
    darksword21  
       2023-07-25 16:47:11 +08:00
    @sunhuawei 不错,还有 wire 和 i18n 顺便学习一下
    houshuu
        48
    houshuu  
       2023-07-25 17:43:19 +08:00 via iPhone
    好的结构应该是没接触过项目的人一看就知道某个功能大概在哪里,而不是必须要 ide 找 defination 翻好多次。
    组里有一些老的 go 项目 ddd 分的巨细,看起来巨累,改也不好改。有些很平的看上去文件多,其实很好理解,按照文件名把模块分开了,一眼就知道改哪里看哪里
    lanlanye
        49
    lanlanye  
       2023-07-26 00:29:06 +08:00
    1. ddd 没有明确代码结构,也不需要
    2. go 语言写业务代码挺坐牢的,结构尽可能简单比较好
    3. Java 那种组织代码的方式和 Go 导入包的方式有那么一点点冲突,写起来会不太舒服
    4. 假设你使用 DDD 是为了处理大型项目,那我可以进一步假设这个大型项目会采用微服务,这种情况下代码里只需要一个聚合/服务就够了,外面加 1-N 个适配器
    lanlanye
        50
    lanlanye  
       2023-07-26 00:31:40 +08:00
    https://github.com/laipz8200/gin-template

    顺便贴一下自己的,这套我最近没怎么维护了,而且直接把领域相关的内容全放在一个包下面也不见得好(但至少不会在导入时遇到重名),仅供参考
    yrj
        51
    yrj  
       2023-07-26 07:42:43 +08:00
    从个人理解的角度回答一下楼主,service 下面,文件和文件夹都可以,如果已经分了多个域,那么文件就够用了,否则也可以在 service 下放文件夹,总之复合自己业务的就是最好的,ddd 的思想是解耦和依赖倒置,参考楼上的洋葱图,因为他是为复杂项目设计的,所以楼主写简单项目的时候,会感觉有些“”啰嗦”。也可以看看我的帖子,我写的 go 开源项目,互相学习。
    qloog
        52
    qloog  
       2023-07-26 10:07:55 +08:00
    可以看下这个: https://github.com/go-eagle/eagle
    业务代码基本都是在 internal 目录里
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   4800 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 38ms · UTC 09:45 · PVG 17:45 · LAX 01:45 · JFK 04:45
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.