考虑使用Golang特色的Package结构在Clean Architecture中

GameWith 2019 年的圣诞活动文章!

关于GameWith的重放功能,这是有关于第一卷的后端设计概述。

开场白

这篇文章是在2019年10月24日我进行LT演讲时制作的,
它是下面演讲幻灯片的重写版本。

总结

参考Golang的Clean Architecture,在创建Web应用程序时,存在有关Package结构的知识。

请留意

我不会提及类似golang-standard/project-layout的目录结构,只会讨论应用程序实现的目录结构。

参考了Clean Architecture的设计

干净架构是什么?

这是一种分层架构,它将层与层之间的依赖关系单向化,使每个层之间解耦。有关详细信息,我认为还有其他许多好的文章,所以我将省略。

选择这种方法最大的优点是它可以实现松散耦合,这意味着作为Web应用程序,它可以更好地适应各种响应、不同的基础架构以及所使用的库和框架的变化。

CleanArchitecture.jpg

引用:“清洁架构”。

思考Golang中的清晰架构

如果选择 echo 作为 Web 框架,gorp 作为数据库访问器。
我们只是参考了 Clean Architecture,但并没有严格遵守。
最大的区别在于允许 framework 层和 adapter 层之间有紧密的耦合。
原因是,我觉得以 framework 本身满足单向依赖的方式来使用非常难以开发。
(如果在 adapter 层定义 framework 层的接口,则可以严格遵守)

GameWithCleanArchitecture.jpg

个人来说,我认为这个人相当遵守Clean Architecture。
因此,我有时会觉得为了在层之间进行交互而增加结构体和接口是对功能复杂性过度和繁琐的回应。

Clean Architecture只是理想,所以我认为根据现实情况进行调整是好的。

思考Clean Architecture的Package结构

Golang软件包的规范

    • Packageはnamespaceのような機能を果たす

名前はスネークケースやキャメルケースはNG

ディレクトリ直下のファイルは同じPacakge

裏を返せばディレクトリが違えば別Package
ディレクトリ構成とPackage構成は同義

同じPackageの変数や関数へのアクセスは無制限

GolangのPrivateはPackage内Private

层次结构(未被采纳)

通常情况下,当在互联网上搜索Golang×CleanArchitecture时,出现最多的程序包组织结构就是与此层次结构对应的程序包组织结构。

優先順位如下:層名目錄 > 角色名目錄(Package)> 功能名檔案。

目录(Package)结构示例。

├── adapter
│   ├── controller
│   │   └── user.go
│   ├── gateway
│   │   └── mysql
│   │       └── user.go
│   └── presenter
│       └── json
│           └── user.go
├── application
│   ├── output
│   │   └── user.go\ (interface)
│   ├── repository
│   │   └── user.go\ (interface)
│   └── usecase
│       └── user.go
└── domain
    └── user
        └── entity.go
    • ※ユーザーデータを取得してロジックを通して返すという場合の例です

 

    ※interface周りはこの例だと冗長に感じますが、実際は多様なレスポンスやインフラに対応するために必要なものです

图层结构的优劣势 de

优点

    レイヤーや役割でPackageが別れているので、importで依存関係やメンバーアクセスのルールが縛れる(CleanArchitectureを守りやすい)

不利之处

    • 機能が増えるにつれてPackageが肥大化する

 

    • レイヤー内で他の機能に自由にアクセスが可能

(IDEの補完のサジェストの肥大化)

機能の実装時にディレクトリの行き来が激しくてとにかく辛い…。

层次结构的探讨

如果在小型项目中没有发生层内膨胀的情况,我认为这是一种有效的配置。
好像没有充分利用Golang的PackagePrivate机制…?

這是因為角色Package > 功能File的結構所導致的。
在使用React×Redux時期所採用的Re-ducks結構是相反的思路,
我們可以考慮採用這個結構來解決這個問題。

Re-ducks的组成

Re-ducks是什么意思?

请参考以下链接:https://github.com/alexnm/re-ducks

这不是通过角色来分割目录的配置方式,而是通过功能来分割目录,并在文件名上附加角色名。

换句话说,现在的组织结构是相反的,功能包>角色文件。

包裹的纵向分割和横向分割

在Clean Architecture中,除了角色和功能之外,还有一个层面。
在Clean Architecture的层面概念中,即使划分包也颠覆了Golang的PackagePrivate特性,所以暂时忽略这一点,
而是以角色为基础,考虑重用性的形式来进行分组。

考虑可再利用性的分组标准是基于角色的连接基本上是1:1的进行分组。
(严格来说,可能需要像共享服务这样的共通服务,但我们将其定义为独立的共通功能包)

分组结果如下图所示。

mesh.jpg

层次×重新配置Re-ducks (正在采用)

刚刚像之前一样,参考了Re-ducks进行分组后,得到了类似独立层级的结果。

为了表示这个结果,我们给它取了类似于app(左上图)/ infra(右上图)/ domain(下图)这样独立层级的组名,并在Package中按照角色的名称创建文件。

包目录结构示例

├── app
│   └── user
│       ├── controller.go
│       ├── output.go\ (interface)
│       ├── presenter_json.go
│       └── usecase.go
├── domain
│   └── user
│       └── entity.go
└── infra
    └── user
        ├── datasource_mysql.go\ (gateway)
        └── repository.go\ (interface)
    • ※ユーザーデータを取得してロジックを通して返すという場合の例です

 

    ※interface周りはこの例だと冗長に感じますが、実際は多様なレスポンスやインフラに対応するために必要なものです

使用Layer × Re-ducks架构的优缺点

优点

    • レイヤー構成のデメリットは大体解消できる

 

    • Package内部に閉じ込められる部分が多くなるので、Privateな部分が妥協実装も許容しやすい

(なんだかGolangのPackage仕様を活かしたPackage構成っぽい)

缺点

    • レイヤー違いで同Packageにあるので、メンバーアクセスが縛りづらくなる

実装者によってCleanArchitectureが崩れやすい

importで名前が被るためaliasをつける必要あり

(Kubenetesのコードとかもこうなっていたのでいいかな…)

总结

以不受限于层级的包结构实现,使开发人员感到满意!能够利用Golang包的机制来构建这种结构,感觉非常棒!

然而,在实际实施中,正如所述的缺点,Clean Architecture被打破并变得廉价,而由于Clean Architecture,所以有很多繁琐的文件结构。通过使用自动生成代码,可以减轻这些缺点。

关于Go代码的自动生成,我们的其他成员将在GameWith Advent Calendar 2019上发布相关文章,敬请期待!

bannerAds