是按技术职责还是按领域职责划分模块包? - Reddit


在构建 REST api 时,您会选择:

选项 A:

├── controllers

│   ├── order.go

│   ├── region.go

│   └── user.go

├── models

│   ├── order.go

│   ├── region.go

│   └── user.go

└── repos

├── order.go

├── region.go

└── user.go

选项 B:

├── order

│   ├── order_controller.go

│   ├── order_entity.go

│   └── order_repo.go

├── region

│   ├── region_controller.go

│   ├── region_entity.go

│   └── region_repo.go

└── user

├── user_controller.go

├── user_entity.go

└── user_repo.go


这是如何在不妥协的情况下在松散耦合和高内聚之间找到平衡。
第一个选项适用于非常小范的项目,其中类型很少,“处理程序”很多,像“ToDo”应用程序,演示,单一职责命令行工具这样的幼稚项目适合这种情况,因为凝聚力不会受到太大影响,因为有每个包中的东西很少。

现第二个适用于其他一切:
如果一旦您在这些目录中拥有大量相同命名的.go文件,那么寻找单一类型的实现和跟踪代码将变得非常笨拙。
两打目录和几个文件比几个文件夹更容易推理,每个文件夹中有两打文件,每个文件夹都命名相同。

这就是为什么我从不选择第一种方法,因为随着熵的增加,它几乎总是蜕变为第二种方法,而且当我可以从那里开始的时候,重新构建所有的东西是浪费时间的。
   
 
按团队划分
下面是我们团队的标准布局,我们有一个工具可以保证这一点。
我们的布局是这样的:

├── etc
│   └── pet-api.yaml
├── internal
│   ├── config
│   │   └── config.go
│   ├── handler
│   │   ├── cat_create_handler.go
│   │   ├── dog_create_handler.go
│   │   └── routes.go
│   ├── logic
│   │   ├── cat_create_logic.go
│   │   └── dog_create_logic.go
│   ├── svc
│   │   └── service_context.go
│   └── types
│       └── types.go
└── pet.go

  • etc是yaml配置文件的文件夹
  • config包含配置的 Go 定义
  • handler包含路由的 HTTP 处理程序
  • logic包含每个处理程序的 biz 逻辑
  • svc是从外部传入处理程序的服务上下文,例如数据库实例。
  • types是定义请求和响应类型的文件夹

为什么我把逻辑文件夹和handler处理程序分开,是因为我希望我们的团队尽可能少地把HTTP请求的信息传递给逻辑处理函数。

另外,所有文件夹的布局是由goctl从下面的API定义文件中生成的,命令是goctl api go -api demo.api -style go_zero -dir .

type (
    CatCreateRequest {
        Name string `path:"name"`
    }

    CatCreateResponse {
        Message string `json:"message"`
    }
)

type (
    DogCreateRequest {
        Name string `path:"name"`
    }

    DogCreateResponse {
        Message string `json:"message"`
    }
)

@server(
    prefix: v1
)
service pet-api {
    @handler CatCreateHandler
    post /pets/cats/:name(CatCreateRequest) returns (CatCreateResponse)

    @handler DogCreateHandler
    post /pets/dogs/:name(DogCreateRequest) returns (DogCreateResponse)
}

前缀:v1将/v1添加到服务中的所有路由,它是可选的。

对于单体服务,我们会在内部下添加模型文件夹,用于数据访问代码。

另外,goctl支持自定义生成行为的选项,比如命名风格、将请求分组到不同的包中,甚至将处理程序和逻辑分别压缩到一个文件中。

安装:go install github.com/zeromicro/go-zero/tools/goctl@latest

   
六边形架构
对于微型服务来说,拥有如下软件包:

- cmd
— service_name
— mock_service
- internal
— entities
— errors
— repositories
—— database
—— fxrates_provider
— server
—— http
—— grpc
— usecases

  • cmd - 持有启动你的服务的方法
  • http/grpc等在/server目录下--你的服务的进入点
  • usecases--领域/业务逻辑层
  • mysql/firestone/其他一些api在/repositories目录下--来自外部的任何数据
  • entities--创建并通过各层传递的模型
  • errors--全局错误,使错误检查更容易。

优点。

  • 没有循环的依赖关系
  • 非常清楚地将逻辑分离出来
  • 只要满足预期的接口,就可以互相替换层(你的老板决定明天要支持http? 没问题!)。
  • 更容易测试
  • 更容易用gomock来模拟接口
  • 更容易调试(根据我的经验,更少的面条,更清晰的责任)。

缺点。

  • 一些重复的代码
  • 转换为实体有时是件麻烦的事