Привет, Хабр! У многих разработчиков на .NET вызывает интерес относительно свежий язык программирования Go (Golang). Однако при поиске информации и учебных материалов он может отпугивать. Нам предлагается забыть все удобное и красивое, чему нас научила .NET, и принять что-то новое, но кажущееся непривычным и не всегда приятным.
И к проблеме непривычности добавляется отсутствие качественного материала на русском языке. Большинство книг поверхностно рассматривают стандартные для всех языков ключевые слова, не углубляясь в важные аспекты их внутреннего устройства и работы.
В своей статье я хочу поэтапно описать все необходимые шаги для создания простого микросервиса и представить его в виде шаблона. Так как я сам не являюсь опытным разработчиком на Go, а только изучаю этот язык, мой шаблон предназначен для того, чтобы показать, как примерно выглядит микросервис.
Содержание
В данной статье я не буду углубляться в тонкости языка и объяснять все детали. Статья предназначена для опытных разработчиков других языков, которые изучают Go и которым не хватает примеров и шаблонов для построения API в учебных целях.
Мы создадим проект с нуля, добавим вычитывание конфигурации, настройку инфраструктуры, настройку DI и настройку HTTP-сервера, и всё это запустим.
Создаем проект
Для разработки я буду использовать IDE GoLand.
Создаем новый проект и нас встречает пустой каталог с файлом go.mod, который содержит версию языка и нашем случае - это 1.22
Для организации архитектуры каталогов проекта буду использовать на работки из этой статьи - структурирование проекта на golang
Создаем каталог с стартовой точкой нашего приложения, аналог из .NET файл Program.cs.
cmd/app/main.go
package main
import "fmt"
func main() {
fmt.Println("Hello World")
}
В языке программирования Go, как и во многих других языках, точкой входа в приложение является функция main. Для создания IDE автоматически профиля сборки эта функция должна быть размещена в пакете main. Наименование пакетов в Go является аналогом неймспейсов в .NET.
Настройка конфигурации
Конфигурация проекта осуществляется с помощью environments. Я не буду задавать их в системе или конфигурационном файле проекта, а для удобства использую библиотеку, которая загружает конфигурацию из файла в переменные окружения (Environment Variables).
База данных, которая будет использоваться в проекте postgresql. Дальнейшая ее настройка и поднятие через docker-compose.yml будут в следующих пунктах, а пока занимаемся настройкой вычитки из конфигурационного файла.
Для начала создадим файл для локального запуска конфигурации.
configs/local.env
DB_CONFIG_USER=golang-template-service
DB_CONFIG_PASSWORD=golang-template-service
DB_CONFIG_DBNAME=golang-template-service
DB_CONFIG_HOST=127.0.0.1
DB_CONFIG_PORT=5432
Теперь создадим структуру, которая будет отражать наш конфигурационный файл. Для этого нам потребуется установить необходимый пакет. В терминале, в корневом каталоге нашего проекта, выполните следующую команду.
go get "github.com/joho/godotenv"
Саму структуру, которую мы будем использовать для конфигурации, поместим в каталог internal. В языке Go все, что помещено в каталог internal, может использоваться только в рамках данного проекта и не может быть использовано как подключаемый пакет. Это ограничение действует автоматически благодаря названию каталога.
Создаем структуру для конфигурации базы данных.
internal/config/database/config.go
package database
// Config - Конфигурация для подключения к БД
type Config struct {
User string
Password string
Host string
Port int
DbName string
}
Также создаем helper, для подгрузки конфигурации из файла с помощью установленной библиотеки и вычитки различных типов из environment.
internal/config/config_helper.go
package config
import (
"github.com/joho/godotenv"
"log"
"os"
"strconv"
)
// LoadEnvironment - загрузить из файла конфигурацию в environments
func LoadEnvironment() {
err := godotenv.Load("configs/local.env")
if err != nil {
log.Fatal("Error loading .env file")
}
}
// getEnv - считать environment в формете string
func getEnv(key string, defaultVal string) string {
if value, exists := os.LookupEnv(key); exists {
return value
}
return defaultVal
}
// getEnvAsInt - считать environment в формете int
func getEnvAsInt(name string, defaultVal int) int {
valueStr := getEnv(name, "")
if value, err := strconv.Atoi(valueStr); err == nil {
return value
}
return defaultVal
}
Теперь создаем общий конфиг приложения, который будет с помощью DI внедряться в наши сервисы. Настройка DI будет далее.
internal/config/config.go
package config
import "src/internal/config/database"
// Config - Главный конфиг приложения
type Config struct {
Database *database.Config
}
func NewConfig() *Config {
return &Config{
Database: &database.Config{
User: getEnv("DB_CONFIG_USER", "root"),
Password: getEnv("DB_CONFIG_PASSWORD", "root"),
Host: getEnv("DB_CONFIG_HOST", "localhost"),
Port: getEnvAsInt("DB_CONFIG_PORT", 3306),
DbName: getEnv("DB_CONFIG_DBNAME", ""),
},
}
}
Теперь возвращаемся в функцию main и проверяем вычитывание нашей конфигурации
cmd/app/main.go
package main
import (
"fmt"
"src/internal/config"
)
func main() {
// Вызываем подгрузку конфигурации
config.LoadEnvironment()
// Создаем конфиг
appConfig := config.NewConfig()
fmt.Println(fmt.Sprintf("User: %s", appConfig.Database.User))
fmt.Println(fmt.Sprintf("Host: %s", appConfig.Database.Host))
fmt.Println(fmt.Sprintf("Password: %s", appConfig.Database.Password))
fmt.Println(fmt.Sprintf("DbName: %s", appConfig.Database.DbName))
fmt.Println(fmt.Sprintf("Port: %d", appConfig.Database.Port))
}
Все работает и идем далее.
Настройка HTTP-сервера
Теперь нам необходимо подготовить всё для запуска HTTP-сервера и настройки маршрутизации. Выполните следующую команду в корневом каталоге проекта для установки необходимых пакетов.
go get "github.com/codegangsta/negroni"
go get "github.com/gorilla/mux"
Далее создадим файл заготовку для маршрутизации
api/router/router.go
package router
import (
"github.com/gorilla/mux"
)
// Router - структура описывающие маршрутизацию контроллеров в нашем приложении
type Router struct {
}
// NewRouter Метод для инициализации структуры в DI
func NewRouter() *Router {
return &Router{}
}
// InitRoutes - инициализация маршрутизации API
func (routes *Router) InitRoutes() *mux.Router {
router := mux.NewRouter()
return router
}
После этого создадим файл с настройками сервера, его инициализацией и запуском.
server/server.go
package server
import (
"github.com/codegangsta/negroni"
"src/api/router"
"src/internal/config"
"net/http"
)
// Server - структура сервера
type Server struct {
AppConfig *config.Config
Router *router.Router
}
// NewServer Метод для инициализации структуры в DI
func NewServer(appConfig *config.Config, router *router.Router) *Server {
return &Server{
AppConfig: appConfig,
Router: router,
}
}
// Run - метод для запуска нашего http-сервера
func (server *Server) Run() {
ngRouter := server.Router.InitRoutes()
ngClassic := negroni.Classic()
ngClassic.UseHandler(ngRouter)
err := http.ListenAndServe(":5000", ngClassic)
if err != nil {
return
}
}
Настройка DI
Сейчас не будем запускать сервер и проверять его работу. Сначала настроим DI (внедрение зависимостей) для нашего сервиса. Выполните следующую команду для установки необходимого пакета.
go get "go.uber.org/dig"
Далее создадим app.go файл в котором будет происходит конфигурация нашего DI
internal/app/app.go
package app
import (
"go.uber.org/dig"
"src/api/router"
"src/internal/config"
"src/server"
)
func BuildContainer() *dig.Container {
container := dig.New()
_ = container.Provide(config.NewConfig)
_ = container.Provide(server.NewServer)
_ = container.Provide(router.NewRouter)
return container
}
Теперь, когда контейнер настроен, возвращаемся в main.go, собираем контейнер и запускаем наш HTTP-сервер.
cmd/app/main.go
package main
import (
"src/internal/app"
"src/internal/config"
"src/server"
)
func main() {
// Вызываем подгрузку конфигурации
config.LoadEnvironment()
// Билдим наш контейре с зависимостями
container := app.BuildContainer()
// Запускаем наш HTTP-сервер
err := container.Invoke(func(server *server.Server) {
server.Run()
})
if err != nil {
panic(err)
}
}
Проверяем, что наше приложение не завершается и продолжает работать HTTP-сервер и идем далее.
Настраиваем заготовку репозитория, сервиса, контроллера.
Далее создадим всё необходимое: от репозитория до контроллера, зарегистрируем маршрутизацию и добавим всё это в DI.
Выполняем команду ниже и скачиваем пакет для работы с uuid.
go get "github.com/google/uuid"
Создаем сущность в нашем случае это будет книга
internal/entities/book/book_entity.go
package book
import "github.com/google/uuid"
// Entity - модель в БД для нашей книги
type Entity struct {
Uuid uuid.UUID
Name string
}
Далее создаем репозиторий
internal/repositories/book/book_repository.go
package book
import (
"fmt"
"src/internal/entities/book"
)
// Repository - Структура репозитория
type Repository struct {
database []book.Entity
}
// NewRepository - Метод для регистрации в DI
func NewRepository() *Repository {
return &Repository{
database: make([]book.Entity, 0),
}
}
// Create - добавить книгу
func (repository *Repository) Create(entity book.Entity) {
repository.database = append(repository.database, entity)
fmt.Println(repository.database)
}
Теперь создаем модель для нашего сервиса
internal/models/book/book_create_model.go
package book
// CreateModel - Модель создания книги
type CreateModel struct {
Name string `json:"name" form:"name"`
}
Создадим наш сервис
internal/services/book/book_service.go
package book
import (
"github.com/google/uuid"
bookEntities "src/internal/entities/book"
bookService "src/internal/models/book"
"src/internal/repositories/book"
)
// Service - для работы с книгами
type Service struct {
repository *book.Repository
}
// NewService - метод для регистрации в DI
func NewService(repository *book.Repository) *Service {
return &Service{repository: repository}
}
// Create - метод создания книги
func (service *Service) Create(model *bookService.CreateModel) {
// Создаем сущность
bookEntity := bookEntities.Entity{
Uuid: uuid.New(),
Name: model.Name,
}
service.repository.Create(bookEntity)
}
Базовые вещи подготовлены. Теперь нам нужно создать контроллер, прописать для него маршрутизацию и зарегистрировать всё это в DI.
Создаем наш контроллер
api/controllers/book/book_controller.go
package book
import (
"encoding/json"
"net/http"
bookModels "src/internal/models/book"
"src/internal/services/book"
)
// Controller - контроллер для работы с книгами
type Controller struct {
service *book.Service
}
// NewController - мето для регистрации контроллера DI
func NewController(service *book.Service) *Controller {
return &Controller{
service: service,
}
}
func (controller *Controller) CreateBook(w http.ResponseWriter, r *http.Request) {
request := new(bookModels.CreateModel)
decoder := json.NewDecoder(r.Body)
_ = decoder.Decode(&request)
controller.service.Create(request)
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
}
Теперь создадим маршрутизацию для нашего контроллера
api/controllers/book/book_controller_route.go
package book
import (
"github.com/gorilla/mux"
)
// ControllerRoute настройки маршрутизации для нашего контроллера
type ControllerRoute struct {
Controller *Controller
}
// NewControllerRoute Метод для регистрации в DI
func NewControllerRoute(controller *Controller) *ControllerRoute {
return &ControllerRoute{Controller: controller}
}
// Route добавить в роутер маршрут
func (route *ControllerRoute) Route(router *mux.Router) *mux.Router {
router.HandleFunc("/api/books", route.Controller.CreateBook).Methods("POST")
return router
}
Теперь нам необходимо вернуться в наш router.go и добавить наш контроллер и маршрутизацию.
api/router/router.go
package router
import (
"github.com/gorilla/mux"
"src/api/controllers/book"
)
// Router - структура описывающие маршрутизацию контроллеров в нашем приложении
type Router struct {
BookRoutes *book.ControllerRoute
}
// NewRouter Метод для инициализации структуры в DI
func NewRouter(bookRoutes *book.ControllerRoute) *Router {
return &Router{
BookRoutes: bookRoutes,
}
}
// InitRoutes - инициализация маршрутизации API
func (routes *Router) InitRoutes() *mux.Router {
router := mux.NewRouter()
router = routes.BookRoutes.Route(router)
return router
}
Далее необходимо вернуться в app.go и добавить регистрацию в DI всего, что мы добавили
api/internal/app/app.go
package app
import (
"go.uber.org/dig"
"src/api/controllers/book"
"src/api/router"
"src/internal/config"
bookRepository "src/internal/repositories/book"
bookService "src/internal/services/book"
"src/server"
)
func BuildContainer() *dig.Container {
container := dig.New()
_ = container.Provide(config.NewConfig)
_ = container.Provide(server.NewServer)
_ = container.Provide(router.NewRouter)
buildBook(container)
return container
}
func buildBook(container *dig.Container) {
_ = container.Provide(book.NewController)
_ = container.Provide(book.NewControllerRoute)
_ = container.Provide(bookService.NewService)
_ = container.Provide(bookRepository.NewRepository)
}
Теперь все готово для нашего первоначального запуска и первого запроса
Полетели
Запускаем и с помощью postman отправляем наш первый запрос и видим, что все заработало.
Заключение
В этой части мы создали всю необходимую первоначальную инфраструктуру для нашего сервиса: настроили конфигурацию, внедрение зависимостей (DI) и запуск HTTP-сервера. В следующей части я планирую подключить базу данных, настроить middleware для логирования, добавить Swagger и, возможно, включить ещё несколько полезных элементов.
Также если есть какие-то замечания или пожелания пишите в комментариях, или создавайте PR в проект.
Спасибо за внимание! Надеюсь кому-то данный материал поможет первоначально разобраться.
Комментарии (25)
xemos
02.06.2024 16:28+3Так зачем с .net на go переходить?
ItwithMisha Автор
02.06.2024 16:28Вариантов думаю масса и самый главный, который интересует большинство, что больше платят)
А так как я понял область применения, что из-за того, что он компилируемый и занимает мало место. На нем пишут всякое ПО для устройств, умной колонки, чтобы было возможно быстро заслать обновление.
Также делать классические API Crud сервисы, где требуется высокая производительность и также всякие потоковые штуки, трансляции видео.Ну и в целом интересно что-то новое изучить
asilzhan11
02.06.2024 16:28+1Также делать классические API Crud сервисы, где требуется высокая производительность и также всякие потоковые штуки, трансляции видео.
Это причина перейти с .NET на Go?
ItwithMisha Автор
02.06.2024 16:28Я вообще не топлю за переход с .NET на Go. Причины могут быть разные, мне в целом интересно потыкать что-то новое, вдруг придет откровение)
gudvinr
02.06.2024 16:28+1А так как я понял область применения, что из-за того, что он компилируемый и занимает мало место.
У вас видимо свой интернет. Если бы вы посмотрели размер вашего же бинарника, на основе кода для которого вы статью написали, то заметили бы, что он очень большой.
Один только runtime для hello world будет весить около 3-4мб. Это очень много и на Go пишут точно не из-за малого размера бинарников. Да, есть tinygo, но это не совсем Go и поведение программ может отличаться. Ну и он не очень-то и популярный за пределами DIY.
Также делать классические API Crud сервисы, где требуется высокая производительность и также всякие потоковые штуки, трансляции видео.
Если в такие программы пихать рефлексию не задумываясь, как вы делаете, то высокой производительности можно и не ожидать.
ItwithMisha Автор
02.06.2024 16:28Вам лишь бы поспорить)
Про размер файла я не сравнивал со своим шаблоном микросервиса, а просто отвечал про области применения и где-то читал статью, как после перехода с Java размер бинарника был значительно меньше. Все равно даже если 3-4 мб много, на джаве вероятно будет весить больше.
А что я пихал из рефлексии? Просто добавил DI контейнер, нужны все-таки объективные замеры на сколько падает производительность, если использовать данный пакет
И в целом статья не почему стоит перейти на Go, а позволить потыкать что-то посмотреть в разрезе, как было на исходном языке с классическим ООП.
amironov
02.06.2024 16:28А так как я понял область применения, что из-за того, что он компилируемый и занимает мало место
В .Net сейчас AOT есть, тоже позволяет собрать небольшой бинарик.
snark87
02.06.2024 16:28+5Я вот тоже когда-то переходил из .NET в Go.
Несколько случайных замечаний:- Название модуля в go.mod: обычно оно не src, а github.com/blahblah/blahblahblah. Это имеет значение, если кто-то будет импортировать ваш код. И совсем не обязательно помещать весь код в src.
- Вы начали использовать пакет internal для кода, который не должен переиспользоваться вне проекта, но при этом по какой-то загадочной причине контроллер и роутер не попадают в internal. По-хорошему, практически весь код, кроме main, может (и должен) быть в internal.
- Я очень понимаю желание использовать DI-контейнеры после многолетнего опыта работы с .NET, но по сути для микросервисов (если они, конечно, микро) большой пользы они не приносят - достаточно просто построить дерево зависимостей вручную.- Если уж использовать ООП, то зависимости контроллеров, роутеров итп должны быть от интерфейсов, а не от реализаций. Ну, и тестов нет :-( Зачем городить многослойность контроллер-сервис-репозиторий, если нет ни бизнес-логики, ни доменной модели, ни тестов? А если и делать многослойную архитектуру (с расчетом, что когда-то микросервис станет гигантским монолитом), то почему вдруг сервис зависит от моделей API?
- Зачем экспортировать все поля в структурах таких, как Router, ControllerRoute, Server?
- В методе CreateBook контроллера отсутствует обработка ошибок - ошибки попросту игнорируются, что нехорошо. То же в методе Run сервера, который по-хорошему должен был бы возвращать ошибку.- В методе getEnvAsInt: код на Go читается гораздо удобнее, если сверху вниз - happy path, магистральный сценарий, а все if обрабатывают ошибочные ситуации. Если поменять инвертировать if err == nil, чтобы стало if err != nil, то код станет каноничнее.
Удачи!ItwithMisha Автор
02.06.2024 16:28Благодарю за подробный ответ)
Название модуля в go.mod: обычно оно не src, а github.com/blahblah/blahblahblah. Это имеет значение, если кто-то будет импортировать ваш код. И совсем не обязательно помещать весь код в src.
Это важный момент, спасибо на будущее запишу себе!
Я очень понимаю желание использовать DI-контейнеры после многолетнего опыта работы с .NET, но по сути для микросервисов (если они, конечно, микро) большой пользы они не приносят - достаточно просто построить дерево зависимостей вручную.
Да после .NET очень тянет к DI контейнерам, но согласен, что тут можно обойтись без этого, чтобы лишний раз все не прописывать
Вы начали использовать пакет internal для кода, который не должен переиспользоваться вне проекта, но при этом по какой-то загадочной причине контроллер и роутер не попадают в internal. По-хорошему, практически весь код, кроме main, может (и должен) быть в internal.
Понял, с этим тоже согласен. В принципе наверное у микросервиса можно вынести в пакет, только какие-нибудь контракты, но нужно ли это в гоу)
По валидации параметров я еще не добавлял, хотел далее. Остальные ваще, спасибо, что описали использую для того, чтобы к следующей статье подредактировать проект.
GroVlAn
02.06.2024 16:28+2Ещё про хотелось бы дополнить про internal, не обязательно весь код в нём хранить, в нем нужно хранить код самого проекта, что не будет импортироваться в другой проект, если есть код, который вы собираетесь использовать в другой проект, хорошей практикой будет его хранить в pkg, так вы сразу обозначите, что этот код может использовать кто то ещё
qoonmax
02.06.2024 16:28+2Платят чуть больше в среднем процентов на 10-20%. Мне кажется, если цель поднять зарплату просто чуть дольше поискать вакансию на .NET.
На больших цифрах так вообще не очень интересно. Вот вам сильно критично получать 400к или 330к условно?
В добавок учтите, что статистика дело такое, даже если в Go платят чуть больше, вы можете вполне залутать низ рынка, потому что у вас опыта на Go мало.
Go отличный, но мотивация плохая. Как известно, лучший язык который нужно изучать для роста зарплаты это английский.
wlbm_onizuka
02.06.2024 16:28+1между 400к и 330к разница колоссальная, т.к 250к (подставьте свою цифру) уходит на жизнь. итого разница в 2 раза на свои хотелки
qoonmax
02.06.2024 16:28+1Это уже хитрости сравнения. Конечно 330к и 400к это я большой разрыв взял, но даже если отталкиваться от него как от плохого сценария, то скажу так, задача "Выучить Go" это на несколько лет, на изучение нишевых задач и решений на Go, на получение интересного работодателю опыта.
Ведь на Go не платят за знание синтаксиса и умение написать микросервис с 2-мя крудами. Вероятно вы просядете по зарплате, пока не получите вкусный релевантный опыт. Следовательно часть дохода вы упускаете ежемесячно.
С маленьким опытом в других языках вы в Go никому не нужны, это 1000%. А с большим опытом в другом языке вы вероятно заработаете больше, нежели от смены стека.ItwithMisha Автор
02.06.2024 16:28Я думаю переход с другого языка и изучения с самого начала немного разные вещи. Ведь весь опыт работы на бекенде остается вместе с тобой и остальная инфраструктура не меняется. Типо контейнеры, куберы, кафки, реббиты. Да определенно нужно время, но я думаю вполне себе за пол года можно.
qoonmax
02.06.2024 16:28+1Разумеется это так. Но в глазах работодателя вы .Net Developer переходящий в Go, а не Golang developer. А чтобы претендовать на топ зарплат Go рынка, нужно как минимум год хорошего опыта на Go стеке.
ItwithMisha Автор
02.06.2024 16:28Это да, мне кажется сначала нужно искать в компании Go отделы и просится туда, а там пойдет. Выходит так просто на рынок согласен сложно)
gudvinr
Ваш материал больше отпугивает, чем уже существующая информация.
Вместо того, чтобы переосмыслить своё мышление и писать на Go, вы свои .NET привычки просто затащили в другую среду и получилось это
ItwithMisha Автор
Переосмыслить свое мышление и писать на Go для меня звучит слишком абстрактно, вы можете указать какие моменты, вы считаете неправильными и я бы с удовольствием для себя, что-то подчеркнул.
А просто в очередной раз слышать про переключение мышление, делу не поможет. Для меня язык программирования всего-лишь инструмент и я стараюсь привести решение, к общему формату, которое будет понятно большому числу разработчиков.
Я параллельно работая на .NET стараюсь изучать Go и достаточно сложно просто взять, выкинуть весь предыдущий опыт и начать писать "правильно". По-этому сначала делаю шаблон по классике, а в дальнейшем буду разбирать и что-то улучшать и мне вот в свое время не хватало таких статей, чтобы в целом понять.
lnkiseleva
Так же как и вы перешла, и тоже сначала пробовала ООП практики по привычке использовать. В общем наверное можно и так, но можно попробовать по другому. Что помогло мне это прежде всего чтение исходников стандартной библиотеки и на английском статьи и видео от William Kennedy. Ещё можно почитать например "Функциональное мышление" или что-то похожее, чтобы обновить подход (или как выше писали переосмыслить мышление).
gudvinr
Пила и молоток - тоже инструменты, но вы вместо того чтобы забивать гвоздь молотком пытаетесь им пилить, потому что "стараетесь привести решение к общему формату"
Любым инструментом надо тоже уметь пользоваться. И это касается не только Go, но и другого языка.
Оно не будет понятно никому кроме вас и, возможно, других людей, которые один раз выучили программирование по гайдам для .NET и решили, что программирование - это только так.
В итоге, вы не пишете микросервисы на Go, вы пишете микросервисы, пытаясь натянуть синтаксис Go на парадигмы .NET.
Какому делу? Научить вас использовать инструмент? Так это ваше дело, а не кого-либо ещё. Только вот если вы будете писать так в Go проекте, который разрабатывают люди, которые действительно используют Go, то как минимум, на вас будут косо смотреть.
Почитайте Effective Go, к примеру.
Уже с первых строк становится понятно, что вы используете пакеты как аналог классов, чтобы покрыть отсутствие инкапсуляции на уровне (отсутствующих) классов. Проблема в том, что Go - это не объектно-ориентированный язык.
Плюс вы сразу гвоздями прибили проект к определённой структуре, хотя в этом абсолютно никакой необходимости нет. Можно без проблем выделять сущности в отдельные структуры внутри одного пакета, пока у вас не появляется необходимости в дополнительном ограничении. Вы же не библиотеку пишете, а сервис, где единственный потребитель программных интерфейсов между сущностями - это сам сервис.
Пакеты с одним файлом в них - это антипаттерн, который больше мешает, чем помогает.
Кучу пакетов втянули просто потому что. Никак не поясняя необходимость, опять же скорее всего потому что вы привыкли к тому, что это так делается в .NET и несмотря на то, что функционал некоторых пакетов есть в стандартной библиотеке.
ItwithMisha Автор
Не совсем согласен с данным примером. Если есть какие-то паттерны и подходы ничто не мешает им ложится на множество языков. Go не такой уже и особенной, что на нем все нужно делать, как вы указали.
Причем тут выучили по гайдам. Есть определенная структура построения приложения для того, чтобы когда приходили новые разработчики, видели примерно тоже самое, что и на своей предыдущей работе. Все равно должна быть какая-то структура
Благодарю изучу, но в целом тут общее описания синтаксиса языка без структуры.
Тут сложно сказать, все-таки язык содержит в себе объектно-ориентированные признаки, которые можно и когда требуется нужно использовать. Мы же не на чистом си пишем, где только одни функции и все
С этим я согласен, что нужно пересмотреть раскидывание по пакетам
gudvinr
Если у вас есть паттерны, которые были спроектированы дедами для ООП, это очень даже мешает их использовать для языков, в которых нет ООП.
В том и дело, что я не говорю, как надо делать в каких-то конкретных ситуациях, я говорю, что изучая новую технологию, нужно проявлять гибкость ума, чтобы писать идиоматичный код, который будет понятен и привычен не только вам.
Любой инструмент имеет свои особенности, с которыми надо уметь работать. Если бы человек, который всю жизнь писал на C++ начал писать на .NET так, как он привык, то вы бы наверняка смотрели на код и думали: ну какой же чудак, ну кто так пишет.
Если вы имеете в виду структуру пакетов и директорий, то в документации языка нет ни слова о какой-то особой структуре проектов на Go.
Если речь о том, что "микросервис" какие-то определенные типы должен собой представлять - тут тоже мимо. Каждый пилит то, во что горазд и то, что нужно для конкретного проекта. Не говоря уже о том, что даже готовые фреймворки между собой не совместимы.
В итоге ваша "определённая структура" - это такая же незнакомая для других разработчиков структура, как и любая другая. Потому что никакого стандарта, которому должны следовать все, не существует.
ItwithMisha Автор
Тут возможно, что язык молодой и еще появиться какая-то структурированность) В целом я согласен, что не нужно писать .NET, как на С++ если вы переходите на другой стек.
Все равно в целом нужно видимо больше времени, чтобы подстроиться под новый инструмент. Так что не буду тут спорить, так не являюсь опытным в гоу разработке, просто для себя изучаю и разбираю, почему он так популярен)
gudvinr
First appeared: November 10, 2009; 14 years ago. Это меньше 10 лет разницы с .NET Framework, в принципе во многих странах с такого возраста уже работать можно.