<!-- .slide: style="font-size: 30px;" -->
# Golang 入門 & pos-service 架構分享
---
<!-- .slide: style="font-size: 30px;" -->
## Agenda
- Golang 超級入門
- Why Golang
- 一點基本概念
- Good parts & Weird parts
- 學習資源
- pos-service 架構分享
- 專案架構 & 導覽
- Dependency Injection
- how to develop / review a package
- how to test a package
Note:
目標,可以看懂 go code,可能可以進行 code review
內部分享,都可以打斷討論或補充
---
<!-- .slide: style="font-size: 30px;" -->
## Golang 入門

- 由C語言編寫而成,長得像 C,具有指標的概念
- 需要經過編譯
- 強型別程式語言
Note:
gopher
---
<!-- .slide: style="font-size: 30px;" -->
### Why Golang ?

- 有 Goolge 支持,很潮、開源
- 易學(吧),適合大專案使用(有明確偏好的開發風格)
- 內建併發,易開發多線程程式。有結實的 [standard library](https://pkg.go.dev/std)
- 生態系多元
- 編譯速度快,執行效能高,~~公司說要用~~
---
<!-- .slide: style="font-size: 30px;" -->
### 基本概念 - package
- Every Go program is made up of packages
- Entry point: package main 的 main func.
- package 內互相可見,外部 package 只能使用其他 package 中有 export 的變數/func
- 大寫開頭的 var / func 代表 exported
```language=golang
package main
import "fmt"
func swap(x, y string) (string, string) {
return y, x
}
func main() {
a, b := swap("hello", "world")
fmt.Println(a, b)
}
```
Note:
若 import 其他 package,一定是呼叫他的 exported func,所以是大寫開頭
----
<!-- .slide: style="font-size: 30px;" -->
### 基本概念 - variable
- 用 var 宣告 package / function level 的變數
- 用 := 宣告且賦值 function level 的變數,只活在該 func scope 中,又稱為 short assignment statement
- 只在必要時使用 var,其餘使用 :=
```language=golang
package main
import "fmt"
var i int // 宣告 package level 變數,沒有賦值
var j int = 2 // 宣告且賦值 package level 變數
func main() {
var k bool = false // 宣告且賦值 func level 變數
fmt.Println(i, j, k)
a := 3 // 宣告且賦值 func level 變數
b, c := "true", false // 宣告且賦值多個 func level 變數
fmt.Println(a, b, c)
printVar()
}
func printVar() {
fmt.Println(i, j)
// fmt.Println(k) // undefined: k
// fmt.Println(a, b, c) // undefined: a, b, c
}
// 0 2 false
// 3 true false
// 0 2
```
----
<!-- .slide: style="font-size: 30px;" -->
### 基本概念 - Zero values
- 當我們創造一個變數但沒有賦值時,值會自動為該 type 的 Zero values (不像 js 是 undefined)
- var i int // type 為 int, value 為 0
- 當我們創造一個變數且賦值時,編譯器會知道其 type
- i := 42 // type 為 int, value 為 42
```
0 for numeric types,
0.0 for floats
false for the boolean type,
“” (the empty string) for strings.
nil for pointers, functions, interfaces, slices, channels, and maps
```
----
<!-- .slide: style="font-size: 30px;" -->
### 基本概念 - For & If
- for: golang 裡只有 for,沒有 while,因為 for 可以做到 while
```language=golang
func main() {
sum := 0
for i := 0; i < 10; i++ { // init statement; condition; post statement
sum += i
}
fmt.Println(sum)
}
```
- if: 也可以使用 init statement (optional)
```language=golang
func pow(x, n, lim float64) float64 {
if v := math.Pow(x, n); v < lim { // init statement; condition
return v
} else {
fmt.Println("exceed limit")
}
// can't use v here !
return lim
}
func main() {
fmt.Println(pow(3, 2, 10)) // 9
fmt.Println(pow(3, 3, 20)) // exceed limit, 20
}
```
----
<!-- .slide: style="font-size: 30px;" -->
### 基本概念 - Struct, field, method
- golang 裡沒有 classes,只有 struct
- struct = collection of fields
- method = 定義在 struct (或其他自定義的 type)上的 function
```language=golang
type Vertex struct {
X float64 // filed X
Y float64 // filed Y
}
// func
func Abs(v Vertex) float64 {
return math.Sqrt(v.X*v.X + v.Y*v.Y)
}
// method
func (v Vertex) Abs() float64 {
return math.Sqrt(v.X*v.X + v.Y*v.Y)
}
func main() {
v := Vertex{3, 4}
fmt.Println(Abs(v))
fmt.Println(v.Abs()) // method 更明確地寫出要使用在哪個 receiver type 上
}
```
----
<!-- .slide: style="font-size: 30px;" -->
### 基本概念 - Interface
- 在 interface 中定義需要滿足的 method,但不需要寫出如何實作
- 一個 type 若有實作 interface 中定義的 method ,該 type 就 **隱式的實現** 該 interface
```language=golang
type Abser interface {
Abs() float64
}
type Vertex struct {
X, Y float64
}
func (v Vertex) Abs() float64 {
return math.Sqrt(v.X*v.X + v.Y*v.Y)
}
func main() {
var a Abser // 變數 a 的 type 是 Abser, 值是 nil
v := Vertex{3, 4} // 宣告一個 vertex 的實例
a = v // 因為 vertex 實現 Abser,所以可以將 v assign 給 a,現在 a 值為 v
fmt.Println(a.Abs())
}
```
----
<!-- .slide: style="font-size: 30px;" -->
### 基本概念 - 沒有講到的部分
- 常用型別:slice, map
- 理解 pointers: pass by value / pass by pointer
- 泛型 generics
- Concurrency: Goroutines & channels
---
<!-- .slide: style="font-size: 30px;" -->
### Good parts
- Go 中不能有宣告但未使用的變數,編譯時會直接報錯
- debug 時有點麻煩,但提昇可讀性
- 有內建 gofmt 及好用的 linter
- 統一程式碼風格
- A minimal set of language constructs
- 捨棄作者認為「花俏」、「降低可讀性」的語法 (有好有壞)
- 例如:沒有三元判斷式可以用 QQ
- Values vs Pointers
- 可以做到 pass by values & pointers,自由度高
----
<!-- .slide: style="font-size: 30px;" -->
### Weird parts - error handling
- Error Checking & Ok checking everywhere: 因為 golang 沒有 throw...catch 的語法
```language=golang
func (service *Service) UpsertStaffToken(
accessToken string,
refreshToken string,
userProfile *sloauth.UserProfile,
) (*StaffToken, error) {
encryptedAccessToken, err := service.encryptTokenService.Encrypt(accessToken)
if err != nil {
return nil, err
}
encryptedRefreshToken, err := service.encryptTokenService.Encrypt(refreshToken)
if err != nil {
return nil, err
}
staffToken, err := service.staffTokenModel.UpsertStaffToken(encryptedAccessToken, encryptedRefreshToken, userProfile)
if err != nil {
return nil, err
}
return staffToken, nil
}
```
----
<!-- .slide: style="font-size: 30px;" -->
### Weird parts - time format

----

```
2006 = YYYY, 01 = MM, 02 = dd, 15 = HH, 04 = mm, 05 = ss
```

---
<!-- .slide: style="font-size: 30px;" -->
### 學習資源 & Best practice
- A Tour of go (互動式):https://go.dev/tour/welcome/1
- official tutorials:https://go.dev/doc/tutorial/
- online playground: https://go.dev/play/
- go 101: https://go101.org
- 中文版: https://gfw.go101.org
- Learn to become a Go developer: https://roadmap.sh/golang
- standard library & third-party packages: https://pkg.go.dev
- Effective Go (稍微過時,仍有參考價值): https://go.dev/doc/effective_go
- Go Code Review Comments: https://github.com/golang/go/wiki/CodeReviewComments
- Go styleguide: https://google.github.io/styleguide/go/decisions
---
<!-- .slide: style="font-size: 30px;" -->
## pos-service 架構分享
- repo: [pos-service](https://bitbucket.org/starlinglabs/pos-service/src/master/)
---
<!-- .slide: style="font-size: 30px;" -->
### 專案架構 & 導覽
- pos-service 是個包含前後端的 monorepo
- 後端 part 在 packages > api 底下,使用 [golang-standards/project-layout](https://github.com/golang-standards/project-layout) 架構
- 專案內使用的 package 放在 internal 底下,每個 package 都是高內聚的單位
- 可以獨立抽出 library 的 package 放在 pkg 底下
- entry point 在 cmd > api / cronjob / sqs-worker > main.go > func main
---
<!-- .slide: style="font-size: 30px;" -->
### Dependency Injection

- 直接在 ClassA 裡引用 / 產生出 services,會導致高耦合、測試不易
- 讓 services 成為 classA 的依賴,並透過 interface 層降低耦合
----
<!-- .slide: style="font-size: 30px;" -->
### Dependency Injection

```language=golang
// 定義 target (ClassA) 及其依賴的 interface (Iservice)
type ClassA struct {
serviceA IServiceA
serviceB IServiceB
}
type IServiceA interface {
methodA()
}
type IServiceB interface {
methodB()
}
// Provider / Builder: 產生 target (ClassA) 的 function
func ProvideClassA (serviceA IServiceA, serviceB IServiceB) ClassA {
return ClassA{serviceA, serviceB}
}
// 在實際 app 運作時
classA = ProvideClassA(realServiceA, realServiceB)
// 在測試時
classAToBeTest = ProvideClassA(mockServiceA, mockServiceB)
```
----
<!-- .slide: style="font-size: 30px;" -->
### DI in pos-service

---
<!-- .slide: style="font-size: 30px;" -->
### how to develop / review a package
- example: add a new api endpoint, for getting merchant's product-category
- 註冊 router & endpoint 的關係
- 實作 product-category 的 controller, service
- 實現 oa 拿 product-category 的方法

----
<!-- .slide: style="font-size: 30px;" -->
### how to develop / review a package


---
<!-- .slide: style="font-size: 30px;" -->
### how to test a package

---
<!-- .slide: style="font-size: 30px;" -->
## List of packages useses
- [gin-gonic](https://github.com/gin-gonic/gin#gin-web-framework): Web Framework
- [mgm](https://github.com/Kamva/mgm): mongo go models
- [go-i18n](https://github.com/nicksnyder/go-i18n): i18n client
- [ginkgo](https://github.com/onsi/ginkgo) & [gomega](https://github.com/onsi/gomega) & [testify](https://github.com/stretchr/testify): test framework and related
- [oso](https://github.com/osohq/oso): authorization framework
- [liquid](https://github.com/osteele/liquid): template framework
詳情請看 `go.mod`
---
<!-- .slide: style="font-size: 30px;" -->
## Q&A

{"title":"Golang 入門 & pos-service 架構分享","description":"Golang 超級入門","contributors":"[{\"id\":\"877c0604-9ea2-40ff-8834-5ea8a760ce56\",\"add\":12556,\"del\":3339}]"}