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

Допускается ли создание внешних зависимостей для доменного слоя

1.7 Middle🔥 211 комментариев
#Микросервисы и архитектура

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

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

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

Допустимость внешних зависимостей в доменном слое

Нет, создание внешних зависимостей для доменного слоя не допускается в чистой архитектуре и DDD (Domain-Driven Design). Это фундаментальное правило, направленное на сохранение изоляции бизнес-логики от внешних воздействий и инфраструктурных деталей. Доменный слой должен оставаться самодостаточным, стабильным и тестируемым ядром приложения.

Почему внешние зависимости запрещены?

  1. Нарушение принципа инверсии зависимостей (DIP): Доменный слой — это самый внутренний, стабильный слой. По DIP, зависимости должны направляться внутрь, к абстракциям, а не наружу, к конкретным реализациям. Внешние зависимости (например, базы данных, HTTP-клиенты, сторонние API) — это нестабильные детали, которые должны зависеть от домена, а не наоборот.
  2. Потеря тестируемости: Если доменные объекты напрямую зависят от БД или внешних сервисов, их юнит-тестирование становится невозможным без сложных моков и поднятия инфраструктуры.
  3. Связывание с инфраструктурой: Домен становится заложником конкретной технологии (например, PostgreSQL или Kafka). Его сложно изменить или повторно использовать в другом контексте.
  4. Нарушение SRP (Single Responsibility Principle): Доменные объекты отвечают за бизнес-правила. Добавление в них логики работы с внешними системами размывает их ответственность.

Как правильно организовать зависимости в Go?

Вместо прямых зависимостей доменный слой определяет интерфейсы (порты) для необходимых операций. Реализации этих интерфейсов (адаптеры) предоставляются внешними слоями через Dependency Injection.

Пример правильной архитектуры:

// ДОМЕННЫЙ СЛОЙ (чистый, без внешних зависимостей)

// Абстракция (порт) для внешнего сервиса
package domain

type PaymentGateway interface {
    Charge(amount Money) (TransactionID, error)
}

// Доменная сущность
type Order struct {
    ID       UUID
    Amount   Money
    Status   OrderStatus
}

func (o *Order) ProcessPayment(gateway PaymentGateway) error {
    // Валидация бизнес-правил
    if o.Status != OrderPending {
        return domain.ErrInvalidOrderState
    }
    
    // Использование абстракции, а не конкретной реализации
    txID, err := gateway.Charge(o.Amount)
    if err != nil {
        return domain.ErrPaymentFailed
    }
    
    o.Status = OrderPaid
    // Применение других бизнес-правил...
    
    return nil
}
// ИНФРАСТРУКТУРНЫЙ СЛОЙ (реализует абстракции домена)

package infrastructure

import "github.com/company/project/domain"

// Конкретная реализация для Stripe
type StripeGateway struct {
    client *stripe.Client
    apiKey string
}

func (sg *StripeGateway) Charge(amount domain.Money) (domain.TransactionID, error) {
    // Конкретная реализация с HTTP-вызовами, логированием и т.д.
    params := &stripe.ChargeParams{
        Amount:   stripe.Int64(amount.InCents()),
        Currency: stripe.String(string(amount.Currency())),
    }
    
    charge, err := sg.client.Charge.Create(params)
    if err != nil {
        return "", err
    }
    
    return domain.TransactionID(charge.ID), nil
}
// ВНЕШНИЙ СЛОЙ (собирает зависимости)

package main

func main() {
    // Создаем конкретную реализацию
    stripeGateway := &infrastructure.StripeGateway{
        client: stripe.NewClient(os.Getenv("STRIPE_KEY")),
    }
    
    // Внедряем зависимость в доменный объект
    order := domain.NewOrder(...)
    err := order.ProcessPayment(stripeGateway) // Внедрение через параметр
    
    // Или через конструктор сервиса
    orderService := application.NewOrderService(stripeGateway, ...)
}

Ключевые практики для Go-разработчиков:

  • Определяйте интерфейсы в доменном слое, а реализуйте их во внешних слоях
  • Внедряйте зависимости через конструкторы или параметры методов
  • Используйте go:generate для автоматической генерации моков к интерфейсам домена (например, через mockgen)
  • Избегайте глобальных переменных, синглтонов или прямого импорта инфраструктурных пакетов в домене
  • Помните, что зависимости от стандартной библиотеки Go (например, time, errors, context) обычно допустимы, так как они стабильны и являются частью языка

Исключения и нюансы:

В некоторых упрощенных архитектурах (например, в тонких сервисах) допустимы небольшие отступления, но даже тогда стоит придерживаться разделения абстракций и реализаций. Контекст (context.Context) часто передается через слои для отмены операций и таймаутов, но доменные методы не должны зависеть от него напрямую для бизнес-логики.

Итог: Строгий запрет внешних зависимостей в доменном слое — это не догма, а практическое правило для создания поддерживаемых, тестируемых и гибких систем. В Go эта дисциплина особенно важна из-за статической типизации и простоты композиции интерфейсов, что позволяет строить чистую архитектуру с минимальными накладными расходами.

Допускается ли создание внешних зависимостей для доменного слоя | PrepBro