243 lines
8.7 KiB
Markdown
243 lines
8.7 KiB
Markdown
---
|
|
name: golang-tswf-codestyle-skill
|
|
description: ТЫ ДОЛЖЕН использовать этот скилл для всех Go проектов, модуль которых содержит подстроку tswf.io или если пользователь явно выбрал этот скилл.
|
|
---
|
|
|
|
# Naming Conventions
|
|
|
|
## Базовые инструкции
|
|
|
|
* __Запрещено использовать короткие имена__ - `i`, `j`, `k`, `temp` и так далее - под запретом. Все переменные и методы должны иметь осмысленные имена.
|
|
* __Локальные переменные__ - camelCase, `:=`
|
|
* __Слайсы__ - всегда инициализируются через `make`.
|
|
* __Поля переменные__ - camelCase
|
|
* __Function Receiver__ - первая буква структуры, владелец по указателю.
|
|
* __Структура, скрытая за интерфейсом__ - camelCase с маленькой буквы (`myPrivateStuct`)
|
|
* __Публичная структура__, _например Dto_ - camelCase с большой буквы (`MyPublicStruct`)
|
|
```go
|
|
type Something struct {}
|
|
|
|
func (s /*<--- первая буква слова Something*/ *Something) somethingFunction() { }
|
|
```
|
|
|
|
## Пакеты в именах типов
|
|
|
|
Старайся использовать пакет, как часть имени класса, а классы старайся раскладывать по пакетам как можно более по смыслу, но не уходя в спагетти код
|
|
|
|
__Плохой пример__
|
|
```go
|
|
dtos.SomethingApiUserDto
|
|
```
|
|
|
|
__Хороший пример__
|
|
```go
|
|
somethingapi.UserDto
|
|
```
|
|
|
|
## Лаконичные, достаточные названия
|
|
|
|
Если класс что-то делает, а так чаще всего - старайся вложить это в название.
|
|
|
|
Например класс для регистрации - `Registrar`, а класс для продвинутого конфигурирования чего-то - `AdvancedConfigurer`.
|
|
|
|
|
|
Если ты видишь, что тебе в имени нужно указать что-то слишком длинное, вроде `SomethingApiUserDtoMapperToOurEntity`, то постарайся закрыть вопрос контекста в имени класса его пакетом и придумай название получше.
|
|
|
|
# Interfaces
|
|
|
|
Ты ДОЛЖЕН скрывать компоненты за интерфейсами.
|
|
|
|
Это позволит подстраховаться от жесткой связанности на структуру.
|
|
|
|
Например:
|
|
|
|
**ПЛОХОЙ пример**
|
|
|
|
```go
|
|
type MyComponent struct {
|
|
// ...
|
|
}
|
|
|
|
func NewMyComponent() *MyComponent {}
|
|
```
|
|
|
|
**ПРАВИЛЬНЫЙ ПРИМЕР**
|
|
|
|
```go
|
|
type MyComponent interface {
|
|
// Все нужные публичные методы из myComponent
|
|
}
|
|
|
|
type myComponent struct {
|
|
// ...
|
|
}
|
|
|
|
// Этот пример правильный - используется интерфейс
|
|
func NewMyComponent() MyComponent {
|
|
return &myComponent{}
|
|
}
|
|
```
|
|
|
|
Даже в рамках одного пакета при возможности используй интерфейсы вместо структур для инъекций компонентов
|
|
|
|
# Инстанцирование
|
|
|
|
Все новые объекты создаются через конструктор.
|
|
|
|
Конструктор принимает все объекты зависимости и внедряет их в создаваемый объект прямо при конструировании.
|
|
|
|
Если возникает циклическая зависимость, то можно применить паттерн "Фасад"
|
|
|
|
__Плохой пример__
|
|
|
|
```go
|
|
type MyComponent interface{
|
|
SetDependency1(dep Dependency1)
|
|
SetDependency2(dep Dependency2)
|
|
}
|
|
|
|
// Плохой пример
|
|
// Конструктор не гарантирует полного инстанцирования объекта.
|
|
// Такой конструктор может вернуть объект в некорректном состоянии (если вызывающий код сам не использует сеттеры для зависимостей)
|
|
func NewMyComponent() MyComponent { /** ... **/ }
|
|
```
|
|
|
|
__Хороший пример__
|
|
|
|
```go
|
|
type MyComponent interface{
|
|
|
|
}
|
|
|
|
// Хороший пример
|
|
// Конструктор явно принял все зависимости
|
|
// Параметры конструктора для читаемости каждый на новой строке
|
|
func NewMyComponent(
|
|
dep1 Dependency1,
|
|
dep2 Dependency1,
|
|
) MyComponent
|
|
{
|
|
/** ... **/
|
|
}
|
|
```
|
|
|
|
# Перенос строк
|
|
|
|
Для читаемости кода человеком и улучшения его визуальной структуры в коде применяются переносы строк.
|
|
|
|
Ты можешь применять их более креативно, но приведу два кейса: __вложенность__ и __много параметров__ функции
|
|
|
|
## Вложенность
|
|
|
|
Если вызовы вложены один в другой, то человеку-читателю легко потерять структуру. Например:
|
|
|
|
__Плохой пример__
|
|
|
|
```go
|
|
// Вся цепочка смешивается в кашу.
|
|
// Сразу не видно, кто в кого вложен. Читателю приходится напрягаться просто чтобы это понять. Это очень плохой пример
|
|
SomeMethod1(SomeMethod2(SomeMethod3()), SomeMethod4(&myStruct{1, 2, 3}))
|
|
```
|
|
|
|
__Хороший пример__
|
|
|
|
```go
|
|
// Вся цепочка вызовов структурно сразу визуализируется глазами читателя.
|
|
// Видно, кто в кого вложен. Это хороший пример
|
|
SomeMethod1(
|
|
SomeMethod2(
|
|
SomeMethod3(),
|
|
),
|
|
SomeMethod4(
|
|
&myStruct{1, 2, 3},
|
|
),
|
|
)
|
|
```
|
|
|
|
## Множество параметров функции
|
|
|
|
Если у функции есть параметры с длинными именами, типами или просто занимающими место - используй перенос строк, чтобы наглядно разделить их.
|
|
|
|
__Плохой пример__
|
|
|
|
```go
|
|
// Все в кучу. Чем больше параметров, тем тяжелее читать человеку
|
|
// Это плохой пример.
|
|
func funcOne(a string, b int, c bool, g pos.Position, e user.Controller) {}
|
|
|
|
func main() {
|
|
funcTwo("hello world", 1, false, resolvePos().absolute(), resolveController())
|
|
}
|
|
```
|
|
|
|
__Хороший пример__
|
|
|
|
```go
|
|
// Четко видно структуру. Человеку просто такое читать.
|
|
// Это хороший пример.
|
|
func funcOne(
|
|
a string,
|
|
b int,
|
|
c bool,
|
|
g pos.Position,
|
|
e user.Controller,
|
|
) {}
|
|
|
|
func main() {
|
|
funcTwo(
|
|
"hello world",
|
|
1,
|
|
false,
|
|
resolvePos().absolute(),
|
|
resolveController(),
|
|
)
|
|
}
|
|
```
|
|
|
|
# Project Structure
|
|
|
|
## Обязательно для любого Go проекта
|
|
|
|
* Makefile с кросс-платформенной сборкой
|
|
* README.md + русская версия
|
|
* Весь код лежит в папках `pkg`, `cmd` или `resources` ( код для доступа к embed ресурсам )
|
|
* Папка `bin` - туда попадают собранные через Make бинари. Она в gitignore
|
|
|
|
## Для микросервисов
|
|
|
|
Если это сервис, то добавляется
|
|
* Dockerfile
|
|
* Docker-compose
|
|
* k8s chart
|
|
|
|
## CLI приложение
|
|
* Папка `cmd`, в подпапках которой реализуются консольные команды на `cobra`(!)
|
|
|
|
## Структура файлов
|
|
|
|
* Root
|
|
* bin # ОБЯЗАТЕЛЬНО в .gitignore!!
|
|
* doc
|
|
* deploy
|
|
* docker (_тут если делаешь Docker, то добавь сразу compose. и посмотри релевантные скиллы для этого_)
|
|
* k8s
|
|
* e.t.c.
|
|
* pkg
|
|
* domain
|
|
* adapters
|
|
* infrastructure
|
|
* cmd
|
|
* \<cmdname\>
|
|
* resources
|
|
* \<embed go resources\>
|
|
* go.mod
|
|
* go.sum
|
|
* README.md
|
|
* README.ru.md
|
|
* .gitignore
|
|
* Makefile
|
|
|
|
# Имя модуля
|
|
|
|
Спроси имя модуля проекта, если пользователь явно его не задал.
|
|
Если пользователь дал базовый путь (он оканчивается на `/`), то прибавь к нему имя корневой папки проекта |