Перейти к содержанию

Quick reference

Одностраничная шпаргалка: именные соглашения, стандартные env-переменные, статусы HTTP → sentinel, Kafka topic naming, типовые метрики. Это не первоисточник — за деталями ходи в соответствующие conventions/. Эта страница — для «быстро напомнить формат».

Содержание

HTTP status → sentinel → code

Полный маппинг — в conventions/http-api.md.

Service sentinel HTTP Response code
service.ErrNotFound 404 not_found
service.ErrValidation 400 invalid_input
service.ErrUnauthorized 401 unauthorized
service.ErrForbidden 403 forbidden
service.ErrConflict 409 conflict
service.ErrRateLimit 429 rate_limited
default (unknown) 500 internal

Правила:

  • Никаких http.StatusX прямо в handler'е — только через mapServiceError.
  • 4xx — клиент виноват, retry не поможет (кроме 429).
  • 5xx — сервис виноват, retry имеет смысл (см. patterns/retry-and-circuit-breaker.md).

Kafka: topic naming

Формат: kazmaps.<service>.<entity>.<action>.

kazmaps.user.user.registered
kazmaps.user.user.banned
kazmaps.review.review.created
kazmaps.review.review.updated
kazmaps.review.review.deleted
kazmaps.media.photo.uploaded
kazmaps.media.moderation.approved
  • <service> — publisher-сервис (owner).
  • <entity> — существительное, единственное число.
  • <action>прошедшее время (факт уже произошёл): created, updated, deleted, completed, failed, approved, banned.
  • DLQ: <topic>.dlq. Примеры: kazmaps.review.review.created.dlq.

Подробно — conventions/events.md.

Kafka: envelope metadata

Обязательные поля в Message.Metadata:

Поле Пример
Event-Type review.created
Schema-Version "1"
Correlation-Id ULID
Source-Service review
Published-At RFC3339Nano
traceparent W3C trace context

Message.UUID — отдельно (не в metadata), ULID, уникален per-сообщение.

Собирается через eventmeta.New(ctx, Envelope{...}). Никаких ручных msg.Metadata.Set(...).

Consumer-group naming

Формат: <service>-<handler>.

notification-on-review-created
notification-on-user-banned
review-on-moderation-approved
search-index-on-review-updated
  • Никаких обобщённых имён (consumer, default, main).
  • Один consumer-group — один handler. Два handler'а на разные топики в одном сервисе → две отдельные group.
  • При rename handler'а имя group меняется → consumer начинает с latest offset'а (см. config). Думай заранее.

Метрики: naming и suffix'ы

Формат: <domain>_<action>_<unit>, snake_case.

Suffix Тип Пример
_seconds Histogram kafka_publish_duration_seconds
_bytes Histogram media_upload_bytes
_total Counter user_registrations_total
_ratio Gauge (0..1) cache_hit_ratio
(no suffix) Gauge (состояние) outbox_unpublished_rows

Примеры:

http_request_duration_seconds         # histogram, стандарт
http_requests_total                   # counter, стандарт
user_registrations_total              # counter, бизнес-событие
kafka_consumer_lag                    # gauge, current - committed
outbox_forwarder_lag_seconds          # histogram
outbox_unpublished_rows               # gauge
circuit_breaker_state                 # gauge: 0/1/2 (Closed/HalfOpen/Open)
circuit_breaker_trips_total           # counter
events_published_total                # counter
events_consumed_total                 # counter
messages_poisoned_total               # counter (DLQ)

Подробнее — conventions/observability.md.

Env-переменные: префиксы и дефолты

Префикс env-переменных — имя сервиса uppercase. Пример для review:

REVIEW_HTTP_ADDR=:8007
REVIEW_DB_DSN=postgres://...
REVIEW_DB_POOL_MIN=2
REVIEW_DB_POOL_MAX=20
REVIEW_REDIS_ADDR=redis:6379
REVIEW_KAFKA_BROKERS=kafka:9092
REVIEW_INTERNAL_API_TOKEN=...
REVIEW_GATEWAY_HMAC_KEY=...
REVIEW_LOG_LEVEL=info
REVIEW_OTEL_EXPORTER_OTLP_ENDPOINT=tempo:4317

Стандартные группы:

Префикс Что содержит
<SVC>_HTTP_ ADDR, READ_TIMEOUT, WRITE_TIMEOUT
<SVC>_DB_ DSN, POOL_MIN, POOL_MAX
<SVC>_REDIS_ ADDR, PASSWORD, DB
<SVC>_KAFKA_ BROKERS, CLIENT_ID
<SVC>_INTERNAL_ API_TOKEN
<SVC>_GATEWAY_ HMAC_KEY
<SVC>_LOG_ LEVEL, FORMAT
<SVC>_OTEL_ OTLP_ENDPOINT, SAMPLE_RATE
<SVC>_RATE_ LOGIN_PER_MIN, UPLOAD_PER_HOUR

Подробно — conventions/configuration.md.

pgx: частые паттерны

Single row:

err := pool.QueryRow(ctx, q, id).Scan(&u.ID, &u.Email)
if err != nil {
    if pkgdb.IsNoRows(err) { return nil, pkgdb.ErrNotFound }
    return nil, fmt.Errorf("get user: %w", err)
}

Multiple rows:

rows, err := pool.Query(ctx, q, placeID)
if err != nil { return nil, fmt.Errorf("query: %w", err) }
defer rows.Close()

for rows.Next() {
    var r domain.Review
    if err := rows.Scan(&r.ID, &r.Rating, &r.Text); err != nil {
        return nil, fmt.Errorf("scan: %w", err)
    }
    out = append(out, r)
}
return out, rows.Err()

Batch:

batch := &pgx.Batch{}
for _, id := range ids {
    batch.Queue(`UPDATE ... WHERE id = $1`, id)
}
br := pool.SendBatch(ctx, batch)
defer br.Close()
for range ids {
    if _, err := br.Exec(); err != nil { return err }
}

Транзакция:

err := s.db.InTx(ctx, func(tx pgx.Tx) error {
    if err := s.repo.CreateTx(ctx, tx, ...); err != nil { return err }
    return s.outboxPub.PublishTx(ctx, tx, "outbox", msg)
})

Подробно — conventions/db-pgx.md.

Файловая раскладка сервиса

cmd/server/main.go          — wiring всех зависимостей
internal/
├── config/                 — envconfig, DSN сборка
├── handler/                — HTTP-handler'ы + router.go
├── middleware/             — gatewayAuth, internalToken, rateLimit
├── service/                — бизнес-логика, sentinel ошибки
├── repository/postgres/    — pgx-репозитории (голый SQL)
├── event/                  — publisher, subscriber, handlers
├── domain/                 — типы предметной области
├── log/                    — slog-wrapper, FromCtx, PII-маскер
├── metrics/                — prometheus registry, MustRegister()
├── otel/                   — setup tracer provider
└── health/                 — /healthz, /readyz handler'ы
pkg/
├── db/                     — pgxpool wrapper, InTx, ErrNotFound
├── httpclient/             — RetryClient, CircuitBreaker
├── eventmeta/              — envelope-builder, helper New()
└── pii/                    — маскирование email/phone для логов
migrations/                 — golang-migrate up/down

Подробно — conventions/project-layout.md.

Логирование: обязательные поля

В каждой строке лога:

Поле Значение
service "review"
level info, warn, error (не debug в prod)
time RFC3339Nano
request_id из X-Request-Id header'а
correlation_id из X-Correlation-Id или родителя
trace_id при активном OTel-span
user_id если authenticated
route паттерн (/v1/reviews/{id})

Нельзя в логах: полный email, phone, Authorization header, JWT, password, токены, payment-details. Маскируй через pkg/pii/.

Подробно — conventions/logging.md.

Таймауты

Типовые значения:

Компонент Timeout
HTTP server ReadHeaderTimeout 10s
HTTP server ReadTimeout 30s
HTTP server WriteTimeout 60s
HTTP server IdleTimeout 120s
chi Timeout middleware 30s
Outbound HTTP-client Timeout 5–10s
Postgres statement_timeout 30s (через env)
Redis op timeout 1s
Kafka producer Timeout 10s
Kafka consumer session.timeout.ms 45–60s
Kafka consumer max.poll.interval.ms 300s (не маскировать медленный handler)

Всегда есть ctx.Done() — handler обязан уважать.

Correlation IDs

Три ID, связывающие сигналы в один запрос:

ID Header Где ещё
request_id X-Request-Id ставит chimw.RequestID, per-request
correlation_id X-Correlation-Id ULID, переживает цепочку HTTP→Kafka→HTTP
trace_id traceparent (W3C) OpenTelemetry span context

В логи кладутся все три. В Tempo span автоматически получает trace_id. В Kafka envelope — Correlation-Id и traceparent.

Makefile targets

В каждом сервис-репо:

Target Что делает
make bootstrap .env + go mod download + compose up + миграции
make up docker compose up -d
make down docker compose down (volumes сохраняются)
make clean docker compose down -v (чистый старт)
make rebuild docker compose build && up -d
make logs docker compose logs -f <service>
make migrate-up migrate up
make migrate-down migrate down 1 (только dev/staging)
make psql psql внутри контейнера
make test go test ./... unit-тесты
make test-integration go test -tags=integration ./...
make lint golangci-lint run
make fmt gofmt + goimports

В handbook-репо (этот): make serve, make build, make check — см. ../Makefile.

Запрещённые практики (частое)

Частый checklist на PR-review:

  • Не использовать fmt.Sprintf с user input в SQL — только плейсхолдеры $1.
  • Не логировать Authorization header, email в открытом виде.
  • Не делать cross-schema JOIN между сервисами.
  • Не вызывать Kafka/HTTP внутри InTx — только БД-операции.
  • Не хардкодить секреты и DSN в коде.
  • Не делать offset-пагинацию на публичных list-endpoint'ах.
  • Не оборачивать pgx в ORM.
  • Не ставить InsecureSkipVerify: true в HTTP-клиенте.
  • Не держать in-memory cache без bounded размера.
  • Не go func() { ... }() без отмены по ctx.
  • Не context.Background() в handler'е.
  • Не time.Tick в long-running worker'е — только NewTicker + Stop().
  • Не map[string]any вместо типизированного struct'а.

Связанные разделы

  • conventions/ — полные правила по темам.
  • patterns/ — deep-dive по архитектурным паттернам.
  • how-to/ — пошаговые рецепты.
  • troubleshooting/ — runbook'и.
  • glossary.md — расшифровка терминов.