8.7 KiB
name, description
| name | description |
|---|---|
| golang-tswf-codestyle-skill | ТЫ ДОЛЖЕН использовать этот скилл для всех Go проектов, модуль которых содержит подстроку tswf.io или если пользователь явно выбрал этот скилл. |
Naming Conventions
Базовые инструкции
- Запрещено использовать короткие имена -
i,j,k,tempи так далее - под запретом. Все переменные и методы должны иметь осмысленные имена. - Локальные переменные - camelCase,
:= - Слайсы - всегда инициализируются через
make. - Поля переменные - camelCase
- Function Receiver - первая буква структуры, владелец по указателю.
- Структура, скрытая за интерфейсом - camelCase с маленькой буквы (
myPrivateStuct) - Публичная структура, например Dto - camelCase с большой буквы (
MyPublicStruct)
type Something struct {}
func (s /*<--- первая буква слова Something*/ *Something) somethingFunction() { }
Пакеты в именах типов
Старайся использовать пакет, как часть имени класса, а классы старайся раскладывать по пакетам как можно более по смыслу, но не уходя в спагетти код
Плохой пример
dtos.SomethingApiUserDto
Хороший пример
somethingapi.UserDto
Лаконичные, достаточные названия
Если класс что-то делает, а так чаще всего - старайся вложить это в название.
Например класс для регистрации - Registrar, а класс для продвинутого конфигурирования чего-то - AdvancedConfigurer.
Если ты видишь, что тебе в имени нужно указать что-то слишком длинное, вроде SomethingApiUserDtoMapperToOurEntity, то постарайся закрыть вопрос контекста в имени класса его пакетом и придумай название получше.
Interfaces
Ты ДОЛЖЕН скрывать компоненты за интерфейсами.
Это позволит подстраховаться от жесткой связанности на структуру.
Например:
ПЛОХОЙ пример
type MyComponent struct {
// ...
}
func NewMyComponent() *MyComponent {}
ПРАВИЛЬНЫЙ ПРИМЕР
type MyComponent interface {
// Все нужные публичные методы из myComponent
}
type myComponent struct {
// ...
}
// Этот пример правильный - используется интерфейс
func NewMyComponent() MyComponent {
return &myComponent{}
}
Даже в рамках одного пакета при возможности используй интерфейсы вместо структур для инъекций компонентов
Инстанцирование
Все новые объекты создаются через конструктор.
Конструктор принимает все объекты зависимости и внедряет их в создаваемый объект прямо при конструировании.
Если возникает циклическая зависимость, то можно применить паттерн "Фасад"
Плохой пример
type MyComponent interface{
SetDependency1(dep Dependency1)
SetDependency2(dep Dependency2)
}
// Плохой пример
// Конструктор не гарантирует полного инстанцирования объекта.
// Такой конструктор может вернуть объект в некорректном состоянии (если вызывающий код сам не использует сеттеры для зависимостей)
func NewMyComponent() MyComponent { /** ... **/ }
Хороший пример
type MyComponent interface{
}
// Хороший пример
// Конструктор явно принял все зависимости
// Параметры конструктора для читаемости каждый на новой строке
func NewMyComponent(
dep1 Dependency1,
dep2 Dependency1,
) MyComponent
{
/** ... **/
}
Перенос строк
Для читаемости кода человеком и улучшения его визуальной структуры в коде применяются переносы строк.
Ты можешь применять их более креативно, но приведу два кейса: вложенность и много параметров функции
Вложенность
Если вызовы вложены один в другой, то человеку-читателю легко потерять структуру. Например:
Плохой пример
// Вся цепочка смешивается в кашу.
// Сразу не видно, кто в кого вложен. Читателю приходится напрягаться просто чтобы это понять. Это очень плохой пример
SomeMethod1(SomeMethod2(SomeMethod3()), SomeMethod4(&myStruct{1, 2, 3}))
Хороший пример
// Вся цепочка вызовов структурно сразу визуализируется глазами читателя.
// Видно, кто в кого вложен. Это хороший пример
SomeMethod1(
SomeMethod2(
SomeMethod3(),
),
SomeMethod4(
&myStruct{1, 2, 3},
),
)
Множество параметров функции
Если у функции есть параметры с длинными именами, типами или просто занимающими место - используй перенос строк, чтобы наглядно разделить их.
Плохой пример
// Все в кучу. Чем больше параметров, тем тяжелее читать человеку
// Это плохой пример.
func funcOne(a string, b int, c bool, g pos.Position, e user.Controller) {}
func main() {
funcTwo("hello world", 1, false, resolvePos().absolute(), resolveController())
}
Хороший пример
// Четко видно структуру. Человеку просто такое читать.
// Это хороший пример.
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
Имя модуля
Спроси имя модуля проекта, если пользователь явно его не задал.
Если пользователь дал базовый путь (он оканчивается на /), то прибавь к нему имя корневой папки проекта