---
# System prepended metadata

title: Code convention

---

# Code convention
here we define our `subjective` best practices in how to code in this service.

Values we want to achive:
1. Development experience
2. DRY code when possible, but not always
3. Modularity
4. working code


## code structure
We tries to use the [new design pattern](https://gitlab.com/ruangguru/source/-/tree/master/example-user-api), but currently still migrating from the old stuff

taken from readme 
```
.
├── client
│   ├── api
│       ├── googleapis   
│       ├── user-token-api  
│           ├── dto.go   
│           ├── client.go              
│   ├── grpc
│       ├── rg-notification         # generated from dependency (contract framework)
├── config                          # service envar config
│   ├── config.go    
├── core                            # core logic will be in this folder
│   ├── entity                      # encapsulate enterprise wide rule
│       ├── user.go
│       ├── usertoken.go
│       ├── entity1.go
│       ├── entity2.go
│   ├── module                      # usecase/service layer
│       ├── auth-usecase            # usecase layer to handle user journey in authentication
│       ├── profile-usecase         # usecase layer to handle user journey in profile page / CMS
│       ├── module-service          # best practice using usecase, but we can use service if it's a simple service 
│   ├── repository                  # interface adaptors for usecase to communicate with outermost communication
│       ├── user-repo               # interface for user domain repository
│       ├── token-repo
├── handler                         # An input port (driving port) lets the application core to expose the functionality to the outside of 
                                    # the world. Handler will communicate to module (usecase layer) to serve the purpose
│   ├── api
│       ├── grpc               
│           ├── user                # code generated (contract framework: *pb.go, *.pb.morse.go, etc.)
│       ├── handler.go              # Implement the grpc/rest api handler
│   ├── subscriber
│       ├── handler.go              # Implement the subscriber handler
│   ├── worker
│       ├── handler.go              # Implement the worker handler
├── migrations                      # rg-osc migrations file will be here
│   ├── ...
├── repository
├── pkg                             # Supporting library / script will be written here
│   ├── helpers                     
│   ├── password                    
├── go.mod
├── go.sum
├── main.go
├── service.yaml
└── README.md
```
conventions:
1. Function mapping from handler to usecases is 1to1
2. Entity can contains business logic
3. Focus on the developers experience

## error handling

0. starts error message in lowercase
1. handler matching error uses `errors.Is`
2. error yang di match defined di `./core/module/errors.go`
3. `./repository/*` should not define any error
4. `./repository/*` uses errors from `./core/repo/<repo>.go`
5. return error from handler uses `fmt.Errorf("%w: %v", <module/errors>, returned error)`
6. return error from repository uses `fmt.Errorf("%w: %v", <repo/errors>, returned error)`
7. handler maps all errors from repository to `core/module/errors.go`

### how define the errors
``` go
var (
	ErrInternal = errors.New("error Internal Error")
	ErrUpstream = errors.New("error Internal Service Call Error")
	ErrDBError  = errors.New("error DB Error")

	ErrBadRequest = errors.New("error Bad Request")
    
    ErrWebsocket

	ErrUserBanned            = fmt.Errorf("%w: error Forbidden User Banned", ErrBadRequest)
	ErrUserNotMT             = fmt.Errorf("%w: error Forbidden, not MT", ErrBadRequest)
	ErrThreadContentTooShort = fmt.Errorf("%w: error thread content too short", ErrInternal)
	ErrUnregisteredClient    = fmt.Errorf("%w: unregistered source client", ErrBadRequest)
	ErrInvalidSourceName     = fmt.Errorf("%w: invalid source name", ErrBadRequest)
	ErrEmptyGroupSerial      = fmt.Errorf("%w: groupSerial must not be empty", ErrBadRequest)
	ErrQuestionNotFound      = fmt.Errorf("%w: question not found", ErrBadRequest)
	ErrQuestionNotReported   = fmt.Errorf("%w: question not reported", ErrBadRequest)
	ErrUserBannedError       = fmt.Errorf("%w: error Forbidden User Banned", ErrBadRequest)

```

``` go
--- handler
var threadErrorMap = {
    [module.BadRequest]: pb.CreateForum__BAD_REQUEST.WithMessage,
    [module.InternalErr]: pb.CreateForum__INTERNAL_ERR.WithInternal,
    [module.NotFound]: pb.CreateForum__NOT_FOUND,
    [module.ErrInternal]: pb.CreateForum__INTERNAL_ERR,
}

func (threadError) showMsg(err) {
    return threadErrorMap[err].WithMessage(err)
}

showMsg(ErrUnregisteredClient)

switch err {
    case BadRequest:
        return Bad....
    case BadRequest:
        return Bad....
    case BadRequest:
        return Bad....
    case BadRequest:
        return Bad....
}

if errors.Is(err, BadRequest) {
    return pb.CreateForum__BAD_REQUEST.WithMessage
} else if errors.Is(err, InternalErr) {
    return pb.CreateForum__INTERNAL_ERR.WithInternal
} else if errors.Is(err, NotFound) {
    return pb.CreateForum__NOT_FOUND
} else if errors.Is(err, InternalErr) {
    return pb.CreateForum__INTERNAL_ERR
}



-- how to return

return threadError{}.showMsg(err)

type Errors interface {
    WithMessage(msg string)
    WithMessageInternal(msg string)
c

map[string]Errors

		return pb.CreateForumThreadError__INTERNAL_ERROR.WithMessage(err.Error()).WithMessageInternal(err.Error())


```


## config handling
we can do these stuff but we don't want to, yet.
TODO: add the `stuff`

## variable naming
1. serializer/deserializer/transform functions uses prefix `To` and suffix `Request` or `Response    `
2. use domain (not to be confused with business domain) as a prefix for public packages/variables
3. domains prefix meanings:
    - Err: error
    - uc: usecases
    - client: service or client
        - don't use `cli` as prefix
    - repo: repository
4. use prefix `is` for boolean vars 
5. map vars uses format `<Value>By<Keys>` or `<Keys><Value>Mapping` (in review, left form is preferrable)
6. array variables uses plural form in the most left noun

correct exaples
```go
type QuestionAttachments []QuestionAttachment

serials = "a,b,c" // this is wrong
serialsJoined = "a,b,c" // this is ok
[]serials = [a, b, c] // this is ok
```

## migration

## how to debug

## how to monitor & alert

## tools
### kubectl

use this alias in .zshrc or .bashrc
```bash
alias kcrg='kubectl --context gke_silicon-airlock-153323_asia-southeast1-a_ruangguru-k8s'
alias kcgl='kubectl --context gke_silicon-airlock-153323_asia-southeast1_ase1-glo-infra-1'
alias kcid='kubectl --context gke_silicon-airlock-153323_asia-southeast1_ase1-id-prod-1'
alias kcth='kubectl --context gke_silicon-airlock-153323_asia-southeast1_ase1-th-prod-1'
alias kcvn='kubectl --context gke_silicon-airlock-153323_asia-southeast1_ase1-vn-prod-1'

```

get list of pods
```bash
kcrg|kcid get pods --all-namespaces -lapp=<service-name>-<role>
```

get logs
```bash
kcrg logs <pod-name> -n <namespace> -c <service-name> [-f]
```

get logs from all pods
``` bash
kcid -n production logs  deployment/rubel-forum --all-containers=true --since=1m

```

exec command on pods
``` bash
kcrg exec rg-forum-8b7bd76cd-rvvfb -n production --tty --stdin -- /bin/bash
```

run cf local
```bash
node node_modules/@google-cloud/functions-framework --target=modularComponents
```