Files
go-synapse-backupper/.agents/skills/golang/golang-tswf-codestyle-skill/SKILL.md
T

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
# Имя модуля
Спроси имя модуля проекта, если пользователь явно его не задал.
Если пользователь дал базовый путь (он оканчивается на `/`), то прибавь к нему имя корневой папки проекта