← Назад к вопросам

Какую серверную архитектуру используешь?

2.2 Middle🔥 231 комментариев
#Микросервисы и архитектура#Производительность и оптимизация

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI6 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Архитектурные подходы для Go-сервисов

Как опытный Go-разработчик, я использую комбинированный подход, адаптируя архитектуру под конкретные требования проекта. Основные принципы: простота, производительность, поддерживаемость и масштабируемость.

Базовый каркас: Clean Architecture + слоистая структура

Для большинства серверных приложений я применяю модифицированную Clean Architecture (Роберта Мартина), адаптированную под идиомы Go:

// Пример структуры проекта
project/
├── cmd/              # Точки входа
├── internal/         # Внутренние пакеты
│   ├── domain/       # Сущности и бизнес-правила
│   ├── usecase/      # Сценарии использования
│   ├── repository/   # Интерфейсы репозиториев
│   ├── handler/      # HTTP/GRPC обработчики
│   └── service/      # Доменные сервисы
├── pkg/              # Переиспользуемые пакеты
└── configs/          # Конфигурации

Ключевые архитектурные паттерны

1. Многослойная архитектура (Layered Architecture)

  • Transport layer (HTTP/gRPC/WebSocket) - обработка входящих запросов
  • Application/Business layer - оркестрация бизнес-логики
  • Domain layer - чистые бизнес-сущности и правила
  • Infrastructure layer - работа с БД, внешними сервисами, кэшем

2. Dependency Injection через интерфейсы

Использую явные зависимости через конструкторы, что улучшает тестируемость:

type UserService struct {
    repo    UserRepository
    cache   CacheService
    logger  Logger
}

func NewUserService(repo UserRepository, cache CacheService, logger Logger) *UserService {
    return &UserService{repo, cache, logger}
}

3. CQRS (Command Query Responsibility Segregation)

Для сложных систем разделяю модели для чтения и записи:

// Command сторона
type UserCreator interface {
    CreateUser(ctx context.Context, cmd CreateUserCommand) error
}

// Query сторона  
type UserQuerier interface {
    GetUserByID(ctx context.Context, id string) (*UserView, error)
    ListUsers(ctx context.Context, filter UserFilter) ([]*UserView, error)
}

Технические реализации

Для REST API: Echo или Chi + стандартная библиотека

// Пример с Chi
r := chi.NewRouter()
r.Use(middleware.Logger, middleware.Recoverer)
r.Route("/api/v1", func(r chi.Router) {
    r.Get("/users/{id}", userHandler.GetUser)
    r.Post("/users", userHandler.CreateUser)
})

Для микросервисов: gRPC + Protocol Buffers

syntax = "proto3";

service UserService {
    rpc GetUser(GetUserRequest) returns (UserResponse);
    rpc CreateUser(CreateUserRequest) returns (CreateUserResponse);
}

Для обработки событий: Worker Pool + каналы

func StartWorkerPool(numWorkers int, jobChan <-chan Job) {
    for i := 0; i < numWorkers; i++ {
        go func(workerID int) {
            for job := range jobChan {
                processJob(job)
            }
        }(i)
    }
}

Особенности Go-ориентированной архитектуры

Значимость контекста (context.Context)

Пробрасываю context через все слои для управления таймаутами, cancellation и передачи метаданных:

func (h *UserHandler) GetUser(c echo.Context) error {
    ctx, cancel := context.WithTimeout(c.Request().Context(), 5*time.Second)
    defer cancel()
    
    user, err := h.service.GetUser(ctx, userID)
    // ...
}

Обработка ошибок по уровням

  • Transport layer: HTTP статус-коды и user-friendly сообщения
  • Business layer: доменные ошибки с достаточным контекстом
  • Infrastructure: низкоуровневые ошибки (сетевые, БД)

Конфигурирование через структуры

type Config struct {
    Server   ServerConfig   `yaml:"server"`
    Database DatabaseConfig `yaml:"database"`
    Redis    RedisConfig    `yaml:"redis"`
}

func LoadConfig(path string) (*Config, error) {
    // Загрузка конфигурации
}

Практики масштабирования

  1. Горизонтальное масштабирование: Stateless сервисы + shared ничего (кроме БД/кэша)

  2. Вертикальное разделение: Разделение на read/write реплики БД

  3. Кэширование стратегий:

    • In-memory кэш для горячих данных
    • Redis для распределенного кэша
    • CDN для статики
  4. Асинхронная обработка:

    • RabbitMQ/Kafka для событийной коммуникации
    • Отложенная обработка через workers

Мониторинг и observability

Обязательно включаю в архитектуру:

  • Метрики (Prometheus + Grafana)
  • Трассировка (OpenTelemetry/Jaeger)
  • Логирование структурированное (Zap/Slog с контекстом)
  • Health checks и readiness/liveness пробы

Выбор архитектуры зависит от:

  • Сложности домена (простой CRUD vs сложная бизнес-логика)
  • Требований к производительности (RPS, latency)
  • Команды и экосистемы (знакомые паттерны, инфраструктура)
  • Бюджета и времени (time-to-market vs долгосрочная поддержка)

В Go я ценю баланс между прагматизмом и архитектурной чистотой. Начинаю с минимально работающей архитектуры и усложняю только при реальной необходимости, следуя принципу YAGNI ("You Aren't Gonna Need It").

Какую серверную архитектуру используешь? | PrepBro