Что такое чистая архитектура
1️⃣ Как кратко ответить
Чистая архитектура — это архитектурный подход, который разделяет систему на слои, чтобы минимизировать зависимость между ними и обеспечить легкость тестирования, изменения и расширения. Основная идея заключается в том, чтобы отделить бизнес-логику от инфраструктурных деталей, таких как пользовательский интерфейс и базы данных.
2️⃣ Подробное объяснение темы
Чистая архитектура — это концепция, предложенная Робертом Мартином (известным как Uncle Bob), которая направлена на создание программного обеспечения, которое легко поддерживать и расширять. Основная цель чистой архитектуры — отделить бизнес-логику от инфраструктурных деталей, таких как пользовательский интерфейс, базы данных и внешние API. Это достигается путем разделения системы на слои, каждый из которых имеет четко определенные обязанности и минимальные зависимости от других слоев.
Основные принципы чистой архитектуры
-
Независимость от фреймворков: Архитектура не должна зависеть от конкретных фреймворков. Это позволяет легко заменять или обновлять фреймворки без значительных изменений в бизнес-логике.
-
Тестируемость: Бизнес-логика должна быть легко тестируемой. Это достигается за счет изоляции логики от внешних зависимостей, что позволяет использовать юнит-тесты.
-
Независимость от пользовательского интерфейса: Изменения в пользовательском интерфейсе не должны влиять на бизнес-логику. Это позволяет изменять или заменять интерфейс без изменения основной логики приложения.
-
Независимость от базы данных: Бизнес-логика не должна зависеть от конкретной базы данных или технологии хранения данных. Это позволяет легко менять базу данных или способ хранения данных.
-
Независимость от внешних агентств: Внешние системы и сервисы не должны влиять на бизнес-логику. Это позволяет легко интегрировать или заменять внешние сервисы.
Структура чистой архитектуры
Чистая архитектура обычно представляется в виде концентрических кругов, где каждый круг представляет слой системы. Внутренние круги содержат более абстрактные компоненты, такие как бизнес-логика, в то время как внешние круги содержат более конкретные компоненты, такие как пользовательский интерфейс и базы данных.
-
Внутренний круг (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.
Чистая архитектура помогает создавать системы, которые легко тестировать, изменять и расширять, что делает ее популярной среди разработчиков, стремящихся к созданию качественного программного обеспечения.
🔒 Подпишись на бусти автора и стань Алигатором, чтобы получить полный доступ к функционалу сайта и отслеживать свой прогресс!
Подписаться