логотип PurpleSchool
логотип PurpleSchool

Чистая архитектура в Golang

Автор

Олег Марков

Введение

Приветствую вас в мире чистой архитектуры! Если вы уже сталкивались с такими проблемами, как сложность в изменении кода, трудности с тестированием или просто хотите улучшить структуру вашего проекта, то вы на правильном пути. Давайте разберемся, как чистая архитектура, применяемая в разрабатываемых на Golang приложениях, может помочь вам в решении этих задач.

Чистая архитектура — это подход к построению программного обеспечения, который предоставляет четкое разделение ответственности и минимизирует зависимость между компонентами. Основная цель — облегчить изменения и тестирование кода, сохраняя структуру проекта чистой и понятной.

Основные концепции чистой архитектуры

Разделение уровней

Чистая архитектура основана на принципах, которые позволяют делить программу на уровни. Эти уровни представляют различные степени абстракции и ответственности. Наиболее часто выделяются три основных уровня:

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

Как это может быть представлено в коде? Давайте посмотрим:

Пример с использованием интерфейсов

// UserRepository определяет операции для работы с пользователями
type UserRepository interface {
    Save(user User) error
    FindByID(id string) (User, error)
}

// User - это доменная модель
type User struct {
    ID   string
    Name string
}

В данном примере UserRepository определяет набор операций, которые можно выполнять с пользователями. Это интерфейс, из которого ваш код может взаимодействовать с базой данных или другим постоянным хранилищем.

Инверсия управления

Инверсия управления (IoC) — это принцип, согласно которому выполнение программы контролируется не статическими вызовами, а внешним кодом. В контексте чистой архитектуры это позволяет изолировать доменную логику от зависимостей.

Попробую объяснить это на примере:

// UserDataStore - здесь реализована логика хранения данных
type UserDataStore struct{}

func (uds *UserDataStore) Save(user User) error {
    // Логика сохранения пользователя в базу данных
    return nil
}

func main() {
    var userRepo UserRepository
    userRepo = &UserDataStore{}  // Инжектируем зависимость

    user := User{ID: "123", Name: "Иван"}
    err := userRepo.Save(user)   // Используем интерфейс для выполнения операции
    if err != nil {
        log.Fatal(err)
    }
}

Здесь наш main() создает объект UserDataStore, который соответствует интерфейсу UserRepository, и использует его для сохранения пользователя. Это позволяет легко заменить UserDataStore на другую имплементацию без изменения вашего приложения.

Принцип зависимости

Этот принцип гласит, что зависимости должны указывать на более высокоуровневые политики, а не на детали реализации. Это означает, что низкоуровневые компоненты должны зависеть от абстракций.

Пример зависимости

// PaymentService определяет операции для работы с платежами
type PaymentService interface {
    Charge(amount float64) error
}

// PaymentProcessor - это низкоуровневый компонент
type PaymentProcessor struct{}

func (pp *PaymentProcessor) Charge(amount float64) error {
    // Исполнение операции платежа
    return nil
}

type OrderService struct {
    payment PaymentService  // Зависимость от интерфейса, а не реализации
}

func (os *OrderService) CompleteOrder(amount float64) error {
    return os.payment.Charge(amount)  // Используем интерфейс
}

В этом примере OrderService не зависит от конкретной реализации PaymentProcessor, а только от интерфейса PaymentService. Это позволяет менять реализацию платежного сервиса без изменения логики обработки заказов.

Заключение

Чистая архитектура — это методология, которая помогает нам управлять сложностью больших систем. Разделение ответственности, инверсия управления и зависимость от абстракций делают наш код более гибким и удобным для тестирования. Я искренне надеюсь, что вы нашли эту статью полезной и теперь лучше понимаете, как применить эти принципы в своих проектах на Golang. Успехов в разработке!

Стрелочка влевоФорматирование кода в GolangКаналы (channels) в GolangСтрелочка вправо

Все гайды по Golang

Работа с YAML в GolangПреобразование типов в GolangКонвертация структур в JSON в GolangStrconv в GolangИспользование пакета SQLx для работы с базами данных в GolangРазбираемся с SQL в GolangРазделение строк с помощью функции split в GolangSort в GoПоиск и замена строк в Go - GolangИспользование пакета reflect в GolangРабота с PostgreSQL в GoPointers в GolangПарсинг в GoРабота со списками (list) в GolangПреобразование int в string в GolangРабота с числами с плавающей точкой в GolangРабота с полями в GolangИспользование enum в GolangОбработка JSON в GoЧтение и запись CSV-файлов в GolangРабота с cookie в GolangРегистры в GoКэширование данных в GolangПреобразование byte в string в GolangByte в GoИспользование bufio для работы с потоками данных в GolangДобавление данных и элементов (add) в Go
Логирование в Golang. Zap, Logrus, Loki, GrafanaИспользование pprof в GolangРабота с Docker-контейнерами в GoМеханизмы синхронизации в GolangРабота с пакетом S3 в GolangМониторинг Golang приложений с помощью PrometheusОптимизация проектов на GoПаттерны проектирования в GolangМиграции базы данных в GolangОркестрация контейнеров Go с Kubernetes + DockerGjGo Playground и компилятор GolangИспользование go mod init для создания модулей GolangРабота с переменными окружения (env) в GolangКоманда go build в GolangАвтоматизация Golang проектов — CI/CD с GitLab CI и JenkinsОтладка кода в GolangЧтение и использование конфигурации в приложениях на GolangКомпиляция в GolangАутентификация в GolangКак развернуть Go-приложение на облаке AWS
Сетевые протоколы в GoПеременные в GolangЗначения в GolangДженерик %T и его применение в GolangТипы данных в GolangИспользование tls в GolangИспользование tag в структурах GolangSwitch в GoСтроки в GolangРабота с потоками (stream) в GolangSelect в GoРуны в GoКонвертация строк в числа в GolangРабота с пакетом params в GolangNull, Nil, None, 0 в GoНаименования переменных, функций и структур в GoInt в GolangУстановка GolangЧтение и установка HTTP заголовков в GolangMethods в GolangGoLand — IDE для разработки на Golang от JetBrainsОбработка «not found» в GolangFloat в GolangФлаги командной строки в Go (Golang)Запуск внешних команд в GolangОбработка ошибок в GoИспользование defer в GolangЗначения default в GolangГенерация кода в GoФорматирование кода в GolangЧистая архитектура в GolangКаналы (channels) в GolangПолучение body из HTTP запроса в Golang
Открыть базу знаний