← Назад к вопросам
Какие знаешь принципы чистой архитектуры?
2.0 Middle🔥 121 комментариев
#Архитектура Flutter#ООП и паттерны
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI26 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Какие знаешь принципы чистой архитектуры
Чистая архитектура (Clean Architecture) — это методология проектирования приложений, предложенная Робертом Мартином. Она разделяет приложение на слои с чёткими границами ответственности.
Основная концепция
┌────────────────────────────────┐
│ Presentation (UI) │
├────────────────────────────────┤
│ Application (Use Cases) │
├────────────────────────────────┤
│ Domain (Business Logic) │
├────────────────────────────────┤
│ Infrastructure (External APIs) │
└────────────────────────────────┘
Зависимости только внутрь! →
Presentation → Application → Domain
Infrastructure → Domain
Четыре основных слоя
1. Domain Layer — Центр (бизнес-логика)
Самый независимый слой. Ничего не знает о внешних сервисах.
// domain/entities/user.dart
class User {
final String id;
final String name;
final String email;
User({
required this.id,
required this.name,
required this.email,
});
// Бизнес-логика (валидация)
bool isValidEmail() => email.contains('@');
bool isAdult() => true; // Пример бизнес-логики
}
// domain/repositories/user_repository.dart
abstract class UserRepository {
Future<User?> getUserById(String id);
Future<void> saveUser(User user);
Future<void> deleteUser(String id);
}
// domain/usecases/get_user_usecase.dart
class GetUserUseCase {
final UserRepository repository;
GetUserUseCase(this.repository);
Future<User?> call(String id) {
return repository.getUserById(id);
}
}
2. Application Layer — Бизнес-правила
Координирует Domain. Использует Use Cases.
// application/services/user_service.dart
class UserService {
final UserRepository repository;
final EmailValidator emailValidator;
UserService(this.repository, this.emailValidator);
Future<void> registerUser(String name, String email) async {
// Применяем бизнес-правила
if (!emailValidator.isValid(email)) {
throw InvalidEmailException();
}
final user = User(
id: generateId(),
name: name,
email: email,
);
await repository.saveUser(user);
}
}
// application/dto/user_dto.dart
class UserDTO {
final String id;
final String name;
final String email;
UserDTO.fromEntity(User user)
: id = user.id,
name = user.name,
email = user.email;
}
3. Infrastructure Layer — Реализация деталей
Работа с БД, API, файловой системой.
// infrastructure/repositories/user_repository_impl.dart
class UserRepositoryImpl implements UserRepository {
final UserLocalDataSource localDataSource;
final UserRemoteDataSource remoteDataSource;
UserRepositoryImpl(this.localDataSource, this.remoteDataSource);
@override
Future<User?> getUserById(String id) async {
try {
// Пытаемся получить с сервера
final user = await remoteDataSource.getUserById(id);
// Кешируем локально
await localDataSource.saveUser(user);
return user;
} catch (e) {
// Если сервер не доступен, берём из кеша
return await localDataSource.getUserById(id);
}
}
@override
Future<void> saveUser(User user) async {
await localDataSource.saveUser(user);
// Синхронизируем с сервером
try {
await remoteDataSource.saveUser(user);
} catch (e) {
// Синхронизируем позже
await _markForSync(user.id);
}
}
}
// infrastructure/datasources/user_local_datasource.dart
abstract class UserLocalDataSource {
Future<User?> getUserById(String id);
Future<void> saveUser(User user);
}
class UserLocalDataSourceImpl implements UserLocalDataSource {
final HiveDatabase hive;
UserLocalDataSourceImpl(this.hive);
@override
Future<User?> getUserById(String id) async {
return hive.users.get(id);
}
@override
Future<void> saveUser(User user) async {
await hive.users.put(user.id, user);
}
}
// infrastructure/datasources/user_remote_datasource.dart
abstract class UserRemoteDataSource {
Future<User> getUserById(String id);
Future<void> saveUser(User user);
}
class UserRemoteDataSourceImpl implements UserRemoteDataSource {
final Dio dio;
UserRemoteDataSourceImpl(this.dio);
@override
Future<User> getUserById(String id) async {
final response = await dio.get('/users/$id');
return User.fromJson(response.data);
}
@override
Future<void> saveUser(User user) async {
await dio.post('/users', data: user.toJson());
}
}
4. Presentation Layer — UI/Controllers
BLoC, GetX, Riverpod. Слушает события, обновляет UI.
// presentation/bloc/user_bloc.dart
class UserBloc extends Bloc<UserEvent, UserState> {
final GetUserUseCase getUserUseCase;
UserBloc(this.getUserUseCase) : super(UserInitial()) {
on<FetchUserEvent>(_onFetchUser);
}
Future<void> _onFetchUser(
FetchUserEvent event,
Emitter<UserState> emit,
) async {
emit(UserLoading());
try {
final user = await getUserUseCase(event.userId);
if (user != null) {
emit(UserLoaded(user));
} else {
emit(UserNotFound());
}
} catch (e) {
emit(UserError(e.toString()));
}
}
}
// presentation/pages/user_page.dart
class UserPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return BlocBuilder<UserBloc, UserState>(
builder: (context, state) {
if (state is UserLoading) {
return Center(child: CircularProgressIndicator());
}
if (state is UserLoaded) {
return UserDetailView(user: state.user);
}
if (state is UserError) {
return Center(child: Text('Ошибка: ${state.message}'));
}
return SizedBox();
},
);
}
}
Правила зависимостей (Dependency Rules)
// ❌ ПЛОХО — нарушение правила
class UserPage extends StatelessWidget {
// Presentation имеет доступ к DataSource!
final dio = Dio();
@override
Widget build(BuildContext context) {
// Прямой запрос к API из UI — это плохо
dio.get('/users/123');
return Container();
}
}
// ✅ ХОРОШО — правильная зависимость
class UserPage extends StatelessWidget {
final UserBloc userBloc; // Зависимость от BLoC
UserPage({required this.userBloc});
@override
Widget build(BuildContext context) {
return BlocBuilder<UserBloc, UserState>(
bloc: userBloc,
builder: (context, state) => ...,
);
}
}
Структура проекта
project/
├── lib/
│ ├── domain/
│ │ ├── entities/
│ │ │ └── user.dart
│ │ ├── repositories/
│ │ │ └── user_repository.dart
│ │ └── usecases/
│ │ └── get_user_usecase.dart
│ │
│ ├── application/
│ │ ├── services/
│ │ │ └── user_service.dart
│ │ ├── dto/
│ │ │ └── user_dto.dart
│ │ └── mappers/
│ │ └── user_mapper.dart
│ │
│ ├── infrastructure/
│ │ ├── repositories/
│ │ │ └── user_repository_impl.dart
│ │ ├── datasources/
│ │ │ ├── user_local_datasource.dart
│ │ │ └── user_remote_datasource.dart
│ │ └── models/
│ │ └── user_model.dart
│ │
│ └── presentation/
│ ├── bloc/
│ │ ├── user_bloc.dart
│ │ ├── user_event.dart
│ │ └── user_state.dart
│ ├── pages/
│ │ └── user_page.dart
│ └── widgets/
│ └── user_tile.dart
Ключевые принципы
- Независимость от Framework — Domain не знает о Flutter
- Тестируемость — каждый слой можно тестировать отдельно
- Направление зависимостей — всегда внутрь
- Инверсия управления — dependency injection
- Разделение ответственности — каждый класс = одна задача
Пример правильного потока данных
UI (Flutter Widget)
↓
BLoC (User Management)
↓
Use Case (GetUserUseCase)
↓
Repository Interface
↓
Repository Implementation (Выбор источника данных)
↓
Data Sources (API / Database)
↓
External Services (REST API / SQLite)
Преимущества Clean Architecture
- Масштабируемость — легко добавлять новые функции
- Тестируемость — юнит тесты без мок-фреймворков
- Переиспользуемость — Domain слой используется в мобил, веб, десктоп
- Независимость — замена БД не требует изменения бизнес-логики
- Команда — чёткие границы, разработчики не конфликтуют
Чистая архитектура — это не панацея, но золотой стандарт для серьёзных приложений.