--- 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 * \ * resources * \ * go.mod * go.sum * README.md * README.ru.md * .gitignore * Makefile # Имя модуля Спроси имя модуля проекта, если пользователь явно его не задал. Если пользователь дал базовый путь (он оканчивается на `/`), то прибавь к нему имя корневой папки проекта