4

按技术职责还是按领域职责来构建代码? - Reddit

 2 years ago
source link: https://www.jdon.com/59869
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

按技术职责还是按领域职责来构建代码? - 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来模拟接口
  • 更容易调试(根据我的经验,更少的面条,更清晰的责任)。
缺点。
  • 一些重复的代码
  • 转换为实体有时是件麻烦的事

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK