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

Что такое чистая архитектура

1️⃣ Как кратко ответить

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

2️⃣ Подробное объяснение темы

Чистая архитектура — это концепция, предложенная Робертом Мартином (известным как Uncle Bob), которая направлена на создание программного обеспечения, которое легко поддерживать и расширять. Основная цель чистой архитектуры — отделить бизнес-логику от инфраструктурных деталей, таких как пользовательский интерфейс, базы данных и внешние API. Это достигается путем разделения системы на слои, каждый из которых имеет четко определенные обязанности и минимальные зависимости от других слоев.

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

  1. Независимость от фреймворков: Архитектура не должна зависеть от конкретных фреймворков. Это позволяет легко заменять или обновлять фреймворки без значительных изменений в бизнес-логике.

  2. Тестируемость: Бизнес-логика должна быть легко тестируемой. Это достигается за счет изоляции логики от внешних зависимостей, что позволяет использовать юнит-тесты.

  3. Независимость от пользовательского интерфейса: Изменения в пользовательском интерфейсе не должны влиять на бизнес-логику. Это позволяет изменять или заменять интерфейс без изменения основной логики приложения.

  4. Независимость от базы данных: Бизнес-логика не должна зависеть от конкретной базы данных или технологии хранения данных. Это позволяет легко менять базу данных или способ хранения данных.

  5. Независимость от внешних агентств: Внешние системы и сервисы не должны влиять на бизнес-логику. Это позволяет легко интегрировать или заменять внешние сервисы.

Структура чистой архитектуры

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

  • Внутренний круг (Entities): Содержит бизнес-логические сущности. Эти сущности представляют собой объекты с методами, которые инкапсулируют бизнес-логику.

  • Следующий круг (Use Cases): Содержит сценарии использования, которые определяют, как бизнес-логика взаимодействует с внешним миром. Эти сценарии описывают конкретные действия, которые могут быть выполнены с бизнес-логикой.

  • Далее (Interface Adapters): Содержит адаптеры, которые преобразуют данные из формата, удобного для использования в бизнес-логике, в формат, удобный для внешних систем, и наоборот.

  • Внешний круг (Frameworks and Drivers): Содержит фреймворки и драйверы, такие как пользовательский интерфейс, базы данных и внешние API. Эти компоненты взаимодействуют с системой через интерфейсы, определенные в более внутренних кругах.

Пример кода

Рассмотрим простой пример, чтобы проиллюстрировать, как может быть организована чистая архитектура в Go.

// Внутренний круг: Entities
// Определяем бизнес-логическую сущность
type User struct {
    ID    int
    Name  string
    Email string
}
​
// Метод для обновления email пользователя
func (u *User) UpdateEmail(newEmail string) {
    u.Email = newEmail
}
​
// Следующий круг: Use Cases
// Определяем сценарий использования для обновления email
type UserService struct {
    userRepo UserRepository
}
​
// Интерфейс для репозитория пользователей
type UserRepository interface {
    FindByID(id int) (*User, error)
    Save(user *User) error
}
​
// Метод для обновления email пользователя
func (s *UserService) UpdateUserEmail(userID int, newEmail string) error {
    user, err := s.userRepo.FindByID(userID)
    if err != nil {
        return err
    }
    user.UpdateEmail(newEmail)
    return s.userRepo.Save(user)
}
​
// Далее: Interface Adapters
// Реализация репозитория пользователей
type InMemoryUserRepository struct {
    users map[int]*User
}
​
// Метод для поиска пользователя по ID
func (r *InMemoryUserRepository) FindByID(id int) (*User, error) {
    user, exists := r.users[id]
    if !exists {
        return nil, fmt.Errorf("user not found")
    }
    return user, nil
}
​
// Метод для сохранения пользователя
func (r *InMemoryUserRepository) Save(user *User) error {
    r.users[user.ID] = user
    return nil
}
​
// Внешний круг: Frameworks and Drivers
// Пример использования сервиса в приложении
func main() {
    userRepo := &InMemoryUserRepository{users: make(map[int]*User)}
    userService := &UserService{userRepo: userRepo}
​
    // Добавляем пользователя
    userRepo.Save(&User{ID: 1, Name: "John Doe", Email: "john@example.com"})
​
    // Обновляем email пользователя
    err := userService.UpdateUserEmail(1, "john.doe@example.com")
    if err != nil {
        log.Fatalf("could not update user email: %v", err)
    }
​
    // Выводим обновленного пользователя
    user, _ := userRepo.FindByID(1)
    fmt.Printf("Updated User: %+v\n", user)
}
  • Entities: Здесь User — это бизнес-логическая сущность, которая инкапсулирует данные и методы, связанные с пользователем.
  • Use Cases: UserService — это сценарий использования, который определяет, как обновить email пользователя, используя репозиторий.
  • Interface Adapters: InMemoryUserRepository — это адаптер, который реализует интерфейс UserRepository и управляет хранением пользователей в памяти.
  • Frameworks and Drivers: main — это точка входа в приложение, где создается и используется UserService.

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

Тема: GO: Архитектура
Стадия: Tech

🔒 Подпишись на бусти автора и стань Алигатором, чтобы получить полный доступ к функционалу сайта и отслеживать свой прогресс!

Твои заметки