# Kratos Microservices Framework Guidelines

> Objective: Define standards for building cloud-native Go microservices with Bilibili's Kratos framework, covering architecture, configuration, transport, dependency injection, observability, and testing.

## 1. Overview & Architecture

### When to Use Kratos

- **Kratos** is an opinionated microservices framework with a full-stack scaffold: HTTP/gRPC dual transport, service registration & discovery, OpenTelemetry tracing, Prometheus metrics, and structured configuration management.
- Use Kratos when you need a **unified microservices scaffold** with strong conventions across a team or organization — especially for Go microservices ecosystems in the Chinese cloud-native space (Nacos, etcd, Consul service discovery).
- Kratos enforces **Clean Architecture** — `service` → `biz` → `data` layer boundaries are a core design constraint. Embrace this; do not bypass it.
- For simpler services or single-binary tools, prefer lightweight frameworks (Gin/Echo/Chi) over Kratos's full scaffold.

### Clean Architecture Boundaries

```text

api/              # Protobuf definitions (.proto) + generated code (buf generate)
cmd/              # Application entry points + wire_gen.go (generated by google/wire)
configs/          # YAML configuration files (config.yaml, config_dev.yaml)
internal/
├── biz/          # Business logic (use cases, domain entities, domain errors, interfaces)
├── data/         # Data access layer (repository implementations, DB/cache clients)
├── server/       # HTTP & gRPC server registration and middleware
└── service/      # Service handlers (thin transport adapters — call biz, map errors)

```

- Enforce strict layer boundaries:
  - `service` → calls `biz` use cases. Maps transport requests to domain params.
  - `biz` → defines interfaces for data access. Contains all business rules. Never imports `data`.
  - `data` → implements `biz` interfaces. Contains ORM, Redis, external service clients.
- Violations (e.g., `biz` importing `data` types or `service` calling the DB directly) break testability and are forbidden.

## 2. Project Setup & Dependency Injection

### Getting Started

- Always start from the official `kratos-layout` template:

  ```bash
  kratos new <service-name>
  cd <service-name>
  go mod tidy
  wire gen ./...   # generate dependency injection code
  ```

- Pin the Kratos CLI version in CI and developer tooling. Mismatched CLI/library versions can cause incompatible code generation:

  ```bash
  go install github.com/go-kratos/kratos/cmd/kratos/v2@v2.7.0
  ```

### Google Wire (Dependency Injection)

- Use **Google Wire** for compile-time dependency injection. Wire generates `wire_gen.go` in `cmd/`:

  ```go
  // cmd/server/wire.go
  //go:build wireinject

  func initApp(conf *conf.Bootstrap, logger log.Logger) (*kratos.App, func(), error) {
    panic(wire.Build(
      data.ProviderSet,   // data layer providers
      biz.ProviderSet,    // biz layer providers
      service.ProviderSet, // service layer providers
      server.ProviderSet,  // HTTP & gRPC server providers
      newApp,
    ))
  }
  ```

- Define `ProviderSet` in each layer's `wire.go` file:

  ```go
  // data/wire.go
  var ProviderSet = wire.NewSet(NewData, NewUserRepo)
  ```

- Run `wire gen ./...` after making any changes to providers or constructor signatures. Commit `wire_gen.go` and `wire.go` together.

## 3. Configuration & Secrets

- Use Kratos's `config` package with YAML files. Support **hot-reload** for non-sensitive config values via `config.Watch()`:

  ```go
  bc := &conf.Bootstrap{}
  if err := c.Scan(bc); err != nil { ... }

  c.Watch(func(key string, value config.Value) {
    // handle dynamic config changes (feature flags, log level, etc.)
  })
  ```

- Use the `kratos/config/env` source to inject environment variables into configuration:

  ```bash
  KRATOS_DATABASE_DSN="postgres://..." ./bin/server
  # config.yaml: database.dsn referenced in code via conf.Bootstrap
  ```

- **Never hardcode** environment-specific values (hostnames, ports, credentials) in YAML files committed to version control. Inject via environment variables or secrets manager.
- Use a secrets manager (Vault, AWS Secrets Manager, Kubernetes Secrets) for credentials. Fetch at startup and inject into the config struct. Never log secret values.

## 4. Transport (HTTP & gRPC)

### Protobuf-First API Design

- Define all APIs in **Protobuf** (`.proto` files in `api/`). Use **`buf`** for linting, formatting, and code generation:

  ```yaml
  # buf.gen.yaml
  version: v2
  plugins:
    - plugin: go # generates .pb.go
      out: .
      opt: paths=source_relative
    - plugin: go-grpc # generates _grpc.pb.go
      out: .
      opt: paths=source_relative
    - plugin: go-http # generates _http.pb.go (Kratos HTTP binding)
      out: .
      opt: paths=source_relative
  ```

  Run `buf generate` after any `.proto` change. Never edit generated files manually.
- Use **`protoc-gen-validate`** for request validation — define rules in `.proto` files:

  ```protobuf
  message CreateUserRequest {
    string email = 1 [(validate.rules).string.email = true];
    string name  = 2 [(validate.rules).string = {min_len: 1, max_len: 100}];
  }
  ```

  Enable the validation middleware on both HTTP and gRPC servers.
- Register servers in `cmd/server.go` via the Kratos app builder:

  ```go
  func newApp(logger log.Logger, hs *http.Server, gs *grpc.Server, r registry.Registrar) *kratos.App {
    return kratos.New(
      kratos.Name(ServiceName),
      kratos.Version(Version),
      kratos.Metadata(map[string]string{}),
      kratos.Logger(logger),
      kratos.Server(hs, gs),
      kratos.Registrar(r),
    )
  }
  ```

### Service Discovery

- Integrate with service discovery registries via the `registry.Registrar` interface. Register on startup; deregister on graceful shutdown:

  ```go
  // Consul example
  import consulAPI "github.com/hashicorp/consul/api"
  import "github.com/go-kratos/kratos/contrib/registry/consul/v2"

  client, _ := consulAPI.NewClient(consulAPI.DefaultConfig())
  reg := consul.New(client)
  // inject reg into newApp via Wire
  ```

## 5. Observability, Error Handling & Testing

### Error Handling

- Use `kratos/errors` to define domain error codes with gRPC status codes and HTTP status mappings:

  ```go
  // biz/errors.go
  var (
    ErrUserNotFound    = errors.NotFound("USER_NOT_FOUND", "user not found")
    ErrUsernameTaken   = errors.Conflict("USERNAME_TAKEN", "username already taken")
    ErrUnauthorized    = errors.Unauthorized("UNAUTHORIZED", "authentication required")
  )
  ```

  Return domain errors from `biz`; the transport middleware maps them to HTTP/gRPC status codes automatically.

### Middleware Chain

- Apply Kratos's built-in **middleware chain** on both HTTP and gRPC transports. Always apply `recovery` first:

  ```go
  httpSrv := http.NewServer(
    http.Address(":8080"),
    http.Middleware(
      recovery.Recovery(),           // must be first — catches panics
      tracing.Server(),              // OpenTelemetry trace context
      logging.Server(logger),        // structured request/response logging
      metrics.Server(/* opts */),    // Prometheus latency & error rate
      validate.Validator(),          // protoc-gen-validate business validation
    ),
  )
  ```

### Logging & Metrics

- Use `kratos/log` with a performant backend: **`slog`** (Go 1.21+), **`zap`**, or **`zerolog`**. Inject via Wire:

  ```go
  logger := log.With(log.NewStdLogger(os.Stdout),
    "ts", log.DefaultTimestamp,
    "caller", log.DefaultCaller,
    "service.name", ServiceName,
    "trace.id", tracing.TraceID(),
    "span.id", tracing.SpanID(),
  )
  ```

- Expose **Prometheus metrics** at `/metrics` via the HTTP server. Define SLO-relevant metrics:
  - Request latency histograms by service, method, and status code
  - Error rate counters
  - Dependency health gauges (DB, Redis, downstream services)

### Testing

- Unit-test `biz` use cases with mock implementations of the repository interfaces defined in `biz/`:

  ```go
  type mockUserRepo struct{ mock.Mock }
  func (m *mockUserRepo) FindByID(ctx context.Context, id int64) (*User, error) {
    return m.Called(ctx, id).Get(0).(*User), m.Called(ctx, id).Error(1)
  }
  ```

- Integration-test `data` repositories using Testcontainers for real database instances.
- End-to-end test transport layers via Kratos's built-in test server utilities.
- Run the quality gate in CI:

  ```bash
  buf lint && buf generate    # protobuf validation and generation
  wire gen ./...              # dependency injection generation
  go build ./...              # build check
  go test -race ./...         # tests with race detector
  golangci-lint run ./...     # linting
  ```
