← Назад к вопросам
Что такое Clean Architecture?
3.0 Senior🔥 201 комментариев
#REST API и микросервисы#SOLID и паттерны проектирования
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Clean Architecture: принципы и реализация
Clean Architecture — это архитектурный подход, предложенный Робертом Мартином (Uncle Bob), который обеспечивает создание систем, которые легко тестировать, изменять и поддерживать. Основная идея: изолировать бизнес-логику от деталей реализации.
Концентрические слои архитектуры
Clean Architecture представляет систему как набор концентрических слоёв:
┌─────────────────────────────────────────┐
│ Frameworks & Drivers (Presentation) │ ← Web, UI, Controllers
├─────────────────────────────────────────┤
│ Interface Adapters (Gateways) │ ← Controllers, Presenters
├─────────────────────────────────────────┤
│ Application Business Rules (Use Cases)│ ← Services, Interactors
├─────────────────────────────────────────┤
│ Enterprise Business Rules (Entities) │ ← Domain, Models
└─────────────────────────────────────────┘
Правило зависимостей: The Dependency Rule
Критическое правило: Зависимости ВСЕГДА направлены ВНУТРЬ (к ядру).
- Внешние слои могут зависеть от внутренних
- Внутренние слои НЕ знают о внешних
- Бизнес-логика независима от фреймворков
// ✗ НЕПРАВИЛЬНО: внутренний слой зависит от внешнего
package com.example.domain;
import org.springframework.web.bind.annotation.*; // ❌ Framework в domain!
public class User {
// ...
}
// ✓ ПРАВИЛЬНО: слои зависят внутрь
package com.example.domain;
public class User { // Чистая бизнес-логика
private String name;
private String email;
// Ноль зависимостей от framework
}
Четыре слоя Clean Architecture
1. Entities (Domain Layer) - ЯДРО
// Используется по всей системе
// Ноль зависимостей от framework
public class User {
private Long id;
private String email;
private String password;
public boolean isValidEmail() {
return email != null && email.contains("@");
}
public void setEmail(String newEmail) {
if (!isValidEmail()) {
throw new InvalidEmailException();
}
this.email = newEmail;
}
}
2. Use Cases (Application Business Rules)
// Бизнес-сценарии
// Использует entities
// НЕ знает о базе данных, фреймворке
public class CreateUserUseCase {
private final UserRepository repository;
private final EmailService emailService;
public CreateUserUseCase(UserRepository repo, EmailService email) {
this.repository = repo;
this.emailService = email;
}
public User execute(CreateUserRequest request) {
// Бизнес-логика
User user = new User();
user.setEmail(request.getEmail());
if (repository.existsByEmail(user.getEmail())) {
throw new UserAlreadyExistsException();
}
User saved = repository.save(user);
emailService.sendWelcome(saved); // Асинхронно
return saved;
}
}
3. Interface Adapters (Gateways)
// Конвертирует данные между слоями
// Реализует контракты (interfaces) для repository, gateway
@Repository
public class UserRepositoryImpl implements UserRepository {
@Autowired
private JpaUserRepository jpaRepo; // Spring Data (деталь реализации)
@Override
public User save(User user) {
UserEntity entity = new UserEntity();
entity.setEmail(user.getEmail());
UserEntity saved = jpaRepo.save(entity);
return toDomain(saved);
}
@Override
public boolean existsByEmail(String email) {
return jpaRepo.existsByEmail(email);
}
private User toDomain(UserEntity entity) {
User user = new User();
user.setId(entity.getId());
user.setEmail(entity.getEmail());
return user;
}
}
4. Frameworks & Drivers (Presentation)
// Зависит ОТ ВСЕХ внутренних слоёв
// Фреймворк может меняться
@RestController
@RequestMapping("/api/users")
public class UserController {
@Autowired
private CreateUserUseCase createUserUseCase; // Бизнес-логика
@PostMapping
public ResponseEntity<UserResponse> createUser(@RequestBody CreateUserRequest request) {
User user = createUserUseCase.execute(request); // Вызываем use case
return ResponseEntity.ok(new UserResponse(user));
}
}
Полный пример: структура проекта
src/main/java/com/example/
├── domain/
│ ├── User.java ← Entities, чистая бизнес-логика
│ ├── UserRepository.java ← Interface (контракт)
│ └── exceptions/
│ └── InvalidEmailException.java
├── application/
│ ├── CreateUserUseCase.java ← Use cases
│ └── GetUserUseCase.java
├── adapters/
│ ├── repository/
│ │ ├── UserRepositoryImpl.java
│ │ └── JpaUserRepository.java ← Spring Data (деталь!)
│ └── email/
│ └── EmailServiceAdapter.java
└── presentation/
├── UserController.java ← REST контроллер
└── dto/
├── CreateUserRequest.java
└── UserResponse.java
Преимущества Clean Architecture
// 1. Тестируемость - легко мокировать зависимости
@Test
public void testCreateUser() {
UserRepository mockRepo = mock(UserRepository.class);
EmailService mockEmail = mock(EmailService.class);
CreateUserUseCase useCase = new CreateUserUseCase(mockRepo, mockEmail);
// Тестируем чистую бизнес-логику, БЕЗ БД и фреймворка
}
// 2. Заменяемость - можем менять реализацию
// Была PostgreSQL?
UserRepository repo = new PostgresUserRepository();
// Переходим на MongoDB?
UserRepository repo = new MongoUserRepository();
// Use case не меняется!
// 3. Независимость - бизнес-логика не зависит от фреймворка
// Можно использовать в консольном приложении, REST, GraphQL
SOLID принципы в Clean Architecture
// Single Responsibility
class CreateUserUseCase { } // Только создание пользователя
class GetUserUseCase { } // Только получение
// Open/Closed
interface UserRepository { } // Закрыта для изменений
// Открыта для расширения: PostgreSQL, Mongo, Redis реализации
// Liskov Substitution
UserRepository repo = new PostgresUserRepository(); // Подставляемы
UserRepository repo = new MongoUserRepository();
// Interface Segregation
interface UserRepository { // Только нужные методы
User save(User user);
User findById(Long id);
}
// Dependency Inversion
class CreateUserUseCase {
private UserRepository repo; // Зависит от абстракции
// НЕ зависит от конкретной реализации
}
Частые ошибки
// ❌ ОШИБКА: Use case зависит от фреймворка
public class CreateUserUseCase {
@Autowired // ← Spring в use case!
private UserRepository repo;
}
// ✓ ПРАВИЛЬНО: зависимости через конструктор
public class CreateUserUseCase {
private final UserRepository repo;
public CreateUserUseCase(UserRepository repo) {
this.repo = repo;
}
}
// ❌ ОШИБКА: Entity зависит от ORM
@Entity
@Table(name = "users") // ← JPA в domain!
public class User { }
// ✓ ПРАВИЛЬНО: Entity чист
public class User {
private Long id;
private String email;
}
Clean Architecture обеспечивает долгосрочную maintainability и flexibility приложения, делая бизнес-логику независимой от выбора фреймворков и технологий.