Quick reference
Одностраничная шпаргалка: именные соглашения, стандартные env-переменные,
статусы HTTP → sentinel, Kafka topic naming, типовые метрики. Это не
первоисточник — за деталями ходи в соответствующие conventions/. Эта
страница — для «быстро напомнить формат».
Содержание
- HTTP status → sentinel → code
- Kafka: topic naming
- Kafka: envelope metadata
- Consumer-group naming
- Метрики: naming и suffix’ы
- Env-переменные: префиксы и дефолты
- pgx: частые паттерны
- Файловая раскладка сервиса
- Логирование: обязательные поля
- Таймауты
- Correlation IDs
- Makefile targets
- Запрещённые практики (частое)
- Связанные разделы
HTTP status → sentinel → code
Полный маппинг — в conventions/http-api.
| 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).
Kafka: topic naming
Формат: kazmaps.<service>.<entity> (per-entity). Действие — в заголовке
Event-Type, не в имени топика. Ключ записи = id сущности.
kazmaps.user.account # Event-Type: account.registered/banned/...
kazmaps.user.push_token
kazmaps.review.review # Event-Type: review.created/updated/deleted/replied
kazmaps.media.photo # Event-Type: photo.uploaded/deleted/tagged
kazmaps.catalog.place
kazmaps.indoor.building<service>— publisher-сервис (owner).<entity>— существительное, единственное число.- Действие в
Event-Type, прошедшее время:created,updated,deleted,approved,banned, … - DLQ:
<topic>.dlq. Пример:kazmaps.review.review.dlq.
Подробно — conventions/events.
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 начинает с
latestoffset’а (см. 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.
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.
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.
Файловая раскладка сервиса
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.
Логирование: обязательные поля
В каждой строке лога:
| Поле | Значение |
|---|---|
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.
Таймауты
Типовые значения:
| Компонент | 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-database
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— расшифровка терминов.