Ответ
Да, я знаком и применяю принципы Чистой Архитектуры. Она помогает создавать слабосвязанные, тестируемые и легко поддерживаемые системы, что особенно ценно в долгосрочных проектах.
Главный принцип — правило зависимостей (Dependency Rule): все зависимости направлены внутрь, к ядру бизнес-логики. Внешние слои (БД, UI, фреймворки) зависят от внутренних, но не наоборот.
Типичная структура слоев в Go-проекте:
Entities (Сущности): Ядро приложения. Это структуры Go, описывающие бизнес-объекты (например,
User,Order). Они не зависят ни от чего.Use Cases (Сценарии использования): Содержат бизнес-логику приложения. Определяют интерфейсы для работы с данными (
UserRepository) и реализуют сценарии (UserService), которые зависят только от этих интерфейсов.Interface Adapters (Адаптеры интерфейсов): Преобразуют данные из формата, удобного для внешних систем (HTTP, gRPC), в формат, удобный для Use Cases, и обратно. Сюда входят контроллеры, презентеры и репозитории.
Frameworks & Drivers (Инфраструктура): Внешний слой. Конкретные реализации: веб-сервер (net/http), драйверы БД (pq, mongo-driver), клиенты внешних API. Этот слой реализует интерфейсы, определенные в Use Cases.
Пример реализации с инверсией зависимостей:
// usecase/user.go (внутренний слой)
package usecase
// Интерфейс репозитория, не знает о БД
type UserRepository interface {
GetByID(ctx context.Context, id int) (*entity.User, error)
}
// ... UserService, который использует UserRepository ...
// -----------------------------------------------------
// repository/user_postgres.go (слой адаптеров/инфраструктуры)
package repository
// Конкретная реализация для PostgreSQL
type UserPostgresRepo struct {
db *sql.DB
}
// Метод реализует интерфейс из usecase
func (r *UserPostgresRepo) GetByID(ctx context.Context, id int) (*entity.User, error) {
// ... логика запроса к PostgreSQL ...
}
// -----------------------------------------------------
// main.go (сборка зависимостей)
func main() {
db := connectToDB()
// Внедрение зависимости: конкретная реализация передается туда,
// где ожидается интерфейс.
userRepo := repository.NewUserPostgresRepo(db)
userService := usecase.NewUserService(userRepo)
httpHandler := delivery.NewHandler(userService)
// ... запуск сервера ...
}Преимущества:
- Независимость от фреймворков и БД: Можно заменить PostgreSQL на MongoDB, не меняя бизнес-логику.
- Высокая тестируемость: Легко подменять зависимости (мокать) в тестах.
- Гибкость и масштабируемость: Код проще изменять и расширять.
Недостатки:
- Больше кода (boilerplate): Требует создания множества интерфейсов и структур.
- Избыточность для простых проектов: Может усложнить небольшие CRUD-сервисы.
Я использую этот подход для сложных и долгоживущих проектов, где важна гибкость и поддерживаемость.