├── README.md ├── golang └── arch │ ├── golang_arch_description.md │ ├── Архитектра GoLang приложений-Borders.jpg │ ├── Архитектра GoLang приложений-Clean Architecture.jpg │ └── Архитектра GoLang приложений-Example_ How it works.jpg └── golang_libraries.md /README.md: -------------------------------------------------------------------------------- 1 | # My Notes 2 | 3 | * [Architecture Go Services](golang/arch/golang_arch_description.md) 4 | * [Go Best Practice](https://github.com/alfssobsd/clean-arch-golang-best-practices) 5 | * [Prefer Go Library](golang_libraries.md) 6 | -------------------------------------------------------------------------------- /golang/arch/golang_arch_description.md: -------------------------------------------------------------------------------- 1 | # Golang Architecture Services(Clean Architecture) 2 | 3 | ![Clean Architecture](https://github.com/alfssobsd/notes/blob/main/golang/arch/%D0%90%D1%80%D1%85%D0%B8%D1%82%D0%B5%D0%BA%D1%82%D1%80%D0%B0%20GoLang%20%D0%BF%D1%80%D0%B8%D0%BB%D0%BE%D0%B6%D0%B5%D0%BD%D0%B8%D0%B8%CC%86-Clean%20Architecture.jpg) 4 | 5 | ![Borders Descriptions](https://github.com/alfssobsd/notes/blob/main/golang/arch/%D0%90%D1%80%D1%85%D0%B8%D1%82%D0%B5%D0%BA%D1%82%D1%80%D0%B0%20GoLang%20%D0%BF%D1%80%D0%B8%D0%BB%D0%BE%D0%B6%D0%B5%D0%BD%D0%B8%D0%B8%CC%86-Borders.jpg) 6 | 7 | ![Exaplain](https://github.com/alfssobsd/notes/blob/main/golang/arch/%D0%90%D1%80%D1%85%D0%B8%D1%82%D0%B5%D0%BA%D1%82%D1%80%D0%B0%20GoLang%20%D0%BF%D1%80%D0%B8%D0%BB%D0%BE%D0%B6%D0%B5%D0%BD%D0%B8%D0%B8%CC%86-Example_%20How%20it%20works.jpg) 8 | 9 | 10 | ## Типы сервисов и их структура кода 11 | Есть как минимум следующие типы сервисов 12 | 13 | * Executor - основной сервис хранения бизнес логики в рамках блока системы 14 | * Api-Gateway - сервис предоставления API для внешних систем, Web UI пользователя или Web UI BackOffice 15 | * Scheduler - сервис постановки задач на обработку , обычно общается только с worker 16 | * Worker - сервисы обрабатывающие background задачи 17 | 18 | ### Executor 19 | Структура каталогов модуля 20 | ``` 21 | ├── entrypoints # entrypoints 22 | │ ├── grpc # воходная точка для grpc запросов 23 | │ │ ├── ads_settings_server_impl.go # описательная часть должна находится в другом go модуле 24 | │ │ └── user_server_impl.go 25 | │ └── http # входная точка для http запросов 26 | │ ├── ads_settings_controller.go 27 | │ ├── ads_settings_controller_dto.go # описания DTO для request/response 28 | │ ├── user_manage_controller.go 29 | │ └── user_manage_controller_dto.go 30 | ├── domain # domain - в большенстве случаев не требуется, можно обойтись только usecase 31 | │ ├── ads_comission_calc_domain.go 32 | │ └── ads_comission_calc_domain_dto.go 33 | ├── usecases 34 | │ ├── ads_settings_usecases.go 35 | │ ├── ads_settings_usecases_dto.go # описания DTO для in/out 36 | │ ├── user_manage_usecases.go 37 | │ └── user_manage_usecases_dto.go 38 | ├── dataproviders 39 | │ ├── authservice 40 | │ ├── pg_provider 41 | │ │ ├── ads_settings_repo.go # репозитории для работы с моделями 42 | │ │ ├── models 43 | │ │ │ ├── gen_model.go # автоматически сгенерированные модели с помощью genna https://github.com/dizzyfool/genna 44 | │ │ │ └── model_validation.go # валидация автоматически сгенерированных моделей 45 | │ │ └── user_repo.go # репозитории для работы с моделями 46 | │ └── rabbitmq 47 | │ └── maker_task_provider.go # провайдер для постановки задач в RMQ 48 | ├── go.mod 49 | ├── go.sum 50 | └── main.go # основная точка входа, фактически слой configuration 51 | ``` 52 | 53 | ### Api-Gateway 54 | Структура каталогов модуля 55 | ``` 56 | ├── entrypoints # entrypoints 57 | │ └── http # входная точка для http запросов 58 | │ ├── ads_settings_controller.go 59 | │ └── ads_settings_controller_dto.go # описания DTO для request/response 60 | ├── usecases # занимается переупоковкой запросов и проверкой доступов 61 | │ └── ads_settings_proxy_usecases.go 62 | ├── dataproviders 63 | │ └── myservice 64 | │ └── myservice_client.go # клиент для обращения к сервису 65 | ├── go.mod 66 | ├── go.sum 67 | └── main.go # основная точка входа, фактически слой configuration 68 | ``` 69 | 70 | ### Scheduler 71 | Структура каталогов модуля 72 | ``` 73 | ├── entrypoints # entrypoints 74 | │ └──cron # запуск переодических задач 75 | │ └── cron_contorller.go 76 | ├── usecases 77 | │ ├── periodic_task_scheduler_usecase.go # usecase который выполняет бизнес-логику для постановки задач 78 | │ └── periodic_task_scheduler_usecase_dto.go # описания DTO для in/out 79 | ├── dataproviders 80 | │ ├── rabbitmq 81 | │ │ └── maker_task_provider.go # провайдер для постановки задач в RMQ 82 | │ └── myservice 83 | │ └── myservice_client.go # клиент для обращения к сервису, к получение записей для формирование заданий 84 | ├── go.mod 85 | ├── go.sum 86 | └── main.go # основная точка входа, фактически слой configuration 87 | ``` 88 | 89 | ### Worker 90 | Структура каталогов модуля 91 | ``` 92 | 93 | ├── entrypoints # entrypoints 94 | │ └──rabbitmq # воходная точка для RMQ задач 95 | │ └── task_contorller.go 96 | ├── usecases 97 | │ ├── somthing_task_handler_usescases.go 98 | │ └── somthing_task_handler_usescases_dto.go # описания DTO для in/out 99 | ├── dataproviders 100 | │ └── myservice 101 | │ └── myservice_client.go # клиент для обращения к сервису который необходим в результате выполнения задачи 102 | ├── go.mod 103 | ├── go.sum 104 | └── main.go # основная точка входа, фактически слой configuration 105 | ``` 106 | 107 | ## Примеры кода 108 | ### UseCase 109 | ``` 110 | type adsSettingsUseCases struct { 111 | someRepo pg_provider.SomeRepo 112 | anotherRepo pg_provider.AnotherRepo 113 | } 114 | 115 | func NewAdsSettingsUseCases(someRepo pg_provider.SomeRepo, anotherRepo pg_provider.AnotherRepo) *adsSettingsUseCases { 116 | 117 | return &adsSettingsUseCases{someRepo: someRepo,anotherRepo: anotherRepo} 118 | } 119 | 120 | type AdsSettingsUseCases interface { 121 | ActionOne(ActionOneInDTO) (ActionOneOutDTO, error) 122 | AnotherAction(AnotherActionInDTO) (*uuid.UUID, error) 123 | } 124 | 125 | func (uc *adsSettingsUseCases) ActionOne(in ActionOneInDTO) (ActionOneOutDTO, error) { 126 | log.Info("AdsSettingsUseCases.ActionOne starting...") 127 | 128 | //some logic 129 | return ActionOneOutDTO{}, nil 130 | } 131 | 132 | func (uc *adsSettingsUseCases) AnotherAction(in AnotherActionInDTO) (*uuid.UUID, error) { 133 | log.Info("AdsSettingsUseCases.ActionOne starting...") 134 | 135 | //some logic 136 | 137 | return &newUUID, nil 138 | } 139 | ``` 140 | 141 | ### Entrypoints 142 | ``` 143 | func ProductRoutes(e *echo.Echo, db *sqlx.DB) { 144 | //create repos and usecases 145 | productRepository := postgres.NewProductRepository(db) 146 | productUseCase := usecases.NewProductUseCase(productRepository) 147 | 148 | e.GET("/api/v1/products", func(c echo.Context) error { 149 | return searchProductsController(c, productUseCase) 150 | }) 151 | e.GET("/api/v1/products/:id", func(c echo.Context) error { 152 | return showProductDetailInfoController(c, productUseCase) 153 | }) 154 | 155 | e.POST("/api/v1/products", func(c echo.Context) error { 156 | return createProductController(c, productUseCase) 157 | }) 158 | 159 | e.POST("/api/v1/products/excel", func(c echo.Context) error { 160 | return createProductsFromExcelController(c, productUseCase) 161 | }) 162 | } 163 | 164 | func searchProductsController(c echo.Context, productUseCase usecases.ProductUseCase) error { 165 | log.Info("searchProductsController") 166 | 167 | productList := productUseCase.SearchProductsUseCase() 168 | var responseProductList []entities.HttpProductResponseEntity 169 | responseProductList = []entities.HttpProductResponseEntity{} 170 | 171 | for _, element := range productList { 172 | responseProductList = append(responseProductList, entities.HttpProductResponseEntity{ 173 | ProductId: element.ProductId, 174 | ProductCodeName: element.ProductCodeName, 175 | ProductTitle: element.ProductTitle, 176 | ProductDescription: element.ProductDescrition, 177 | ProductPrice: element.ProductPrice, 178 | }) 179 | } 180 | return c.JSON(http.StatusOK, entities.HttpProductListResponseEntity{ 181 | Total: len(responseProductList), 182 | Offset: 0, 183 | Items: responseProductList, 184 | }) 185 | } 186 | 187 | func showProductDetailInfoController(c echo.Context, productUseCase usecases.ProductUseCase) error { 188 | id := c.Param("id") 189 | item, err := productUseCase.ShowProductDetailInfoUseCase(uuid.FromStringOrNil(id)) 190 | if err != nil { 191 | return c.JSON(http.StatusNotFound, entities.HttpActionResponseEntity{ 192 | Code: http.StatusNotFound, 193 | Message: err.Error(), 194 | }) 195 | } 196 | return c.JSON(http.StatusOK, entities.HttpProductResponseEntity{ 197 | ProductId: item.ProductId, 198 | ProductCodeName: item.ProductCodeName, 199 | ProductTitle: item.ProductTitle, 200 | ProductDescription: item.ProductDescrition, 201 | ProductPrice: item.ProductPrice, 202 | }) 203 | } 204 | 205 | func createProductController(c echo.Context, productUseCase usecases.ProductUseCase) error { 206 | 207 | r := new(entities.HttpProductRequestEntity) 208 | _ = c.Bind(r) 209 | log.Info("createProductController ", r) 210 | 211 | productEntity := productUseCase.CreateProductUseCase(_useCaseEntities.ProductUseCaseEntity{ 212 | ProductTitle: r.ProductTitle, 213 | ProductCodeName: r.ProductCodeName, 214 | ProductPrice: r.ProductPrice, 215 | ProductDescrition: r.ProductDescription, 216 | }) 217 | 218 | return c.JSON(http.StatusOK, entities.HttpProductResponseEntity{ 219 | ProductId: productEntity.ProductId, 220 | ProductCodeName: productEntity.ProductCodeName, 221 | ProductTitle: productEntity.ProductTitle, 222 | ProductDescription: productEntity.ProductDescrition, 223 | ProductPrice: productEntity.ProductPrice, 224 | }) 225 | } 226 | 227 | func createProductsFromExcelController(c echo.Context, productUseCase usecases.ProductUseCase) error { 228 | log.Info("createProductsFromExcelController ") 229 | 230 | //prepare temp file for parsing 231 | file, err := c.FormFile("file") 232 | if err != nil { 233 | return c.JSON(http.StatusBadRequest, err) 234 | } 235 | 236 | src, err := file.Open() 237 | if err != nil { 238 | return c.JSON(http.StatusBadRequest, err) 239 | } 240 | 241 | defer func() { 242 | _ = src.Close() 243 | }() 244 | 245 | tmpfile, err := ioutil.TempFile("", "products.*.xlsx") 246 | if err != nil { 247 | return c.JSON(http.StatusBadRequest, err) 248 | } 249 | 250 | if _, err = io.Copy(tmpfile, src); err != nil { 251 | return c.JSON(http.StatusBadRequest, err) 252 | } 253 | 254 | productsList := productUseCase.CreateProductFromExcelUseCase(tmpfile.Name()) 255 | var responseList []entities.HttpProductResponseEntity 256 | for _, element := range productsList { 257 | responseList = append(responseList, entities.HttpProductResponseEntity{ 258 | ProductId: element.ProductId, 259 | ProductCodeName: element.ProductCodeName, 260 | ProductTitle: element.ProductTitle, 261 | ProductDescription: element.ProductDescrition, 262 | ProductPrice: element.ProductPrice, 263 | }) 264 | } 265 | return c.JSON(http.StatusOK, entities.HttpProductListResponseEntity{ 266 | Total: len(responseList), 267 | Offset: 0, 268 | Items: responseList, 269 | }) 270 | } 271 | ``` 272 | -------------------------------------------------------------------------------- /golang/arch/Архитектра GoLang приложений-Borders.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alfssobsd/notes/c89c619aab60e81a24ce6a92f6b23ada4d2656d0/golang/arch/Архитектра GoLang приложений-Borders.jpg -------------------------------------------------------------------------------- /golang/arch/Архитектра GoLang приложений-Clean Architecture.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alfssobsd/notes/c89c619aab60e81a24ce6a92f6b23ada4d2656d0/golang/arch/Архитектра GoLang приложений-Clean Architecture.jpg -------------------------------------------------------------------------------- /golang/arch/Архитектра GoLang приложений-Example_ How it works.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alfssobsd/notes/c89c619aab60e81a24ce6a92f6b23ada4d2656d0/golang/arch/Архитектра GoLang приложений-Example_ How it works.jpg -------------------------------------------------------------------------------- /golang_libraries.md: -------------------------------------------------------------------------------- 1 | # Prefer Go Library 2 | 3 | |Name|Link|Describe| 4 | |:----|:---|--------| 5 | |Echo| https://github.com/labstack/echo| Web фреймворк для реализации api-gateway| 6 | |Zap| https://github.com/uber-go/zap| Библиотека логирования| 7 | |Viper| https://github.com/spf13/viper| Библиотека для работы с конфигурационными файлами| 8 | |go-pg| https://github.com/go-pg/pg| ORM для работы с PostgreSQL| 9 | |afs |https://github.com/viant/afs| Абстрактная файловая система| 10 | |genna| https://github.com/dizzyfool/genna| Генерато моделей  для go-pg| 11 | |jaeger| https://github.com/jaegertracing/jaeger-client-go| Трассировка логов и запросов| 12 | |grpc| https://github.com/grpc/grpc-go| Библиотека для работы с GRPC| 13 | |grpc/status| https://pkg.go.dev/google.golang.org/grpc/status?utm_source=godoc#pkg-overview |Библиотека для работы со статусами в GRPC 14 | Как работать с GRPC ошибками: https://jbrandhorst.com/post/grpc-errors/| 15 | |protoc-gen-validate| https://github.com/envoyproxy/protoc-gen-validate |Валидация сообщений GRPC| 16 | |namely/protoc-all| https://github.com/namely/docker-protoc |Docker образ для собрки GRPC proto| 17 | |pgmigrate |https://github.com/yandex/pgmigrate |Утилита для работы с миграциями для базы данных PostgreSQL| 18 | |grpc_cli |https://github.com/grpc/grpc/blob/master/doc/command_line_tool.md | Утилита для тестирования GRPC| 19 | |BloomRPC| https://github.com/uw-labs/bloomrpc |Утилита для тестирования GRPC с UI| 20 | |testify| https://github.com/stretchr/testify |Библиотека для создания Mock| 21 | |jwt-go| https://github.com/dgrijalva/jwt-go |Работа с JWT токенами в golang| 22 | |excelize| https://github.com/360EntSecGroup-Skylar/excelize |Библиотека для работы с Excel| 23 | |dig| https://github.com/uber-go/dig |Библиотека для работы с DI (использовать с остарожностью!)| 24 | |casbin| https://github.com/casbin/casbin |Библиотке для проверки прав ( RBAC / ABAC /ACL)| 25 | |cookiecutter| https://github.com/cookiecutter/cookiecutter |Шаблонизатор проектов| 26 | --------------------------------------------------------------------------------