← Назад к вопросам
Какие архитектуры не нравятся?
1.0 Junior🔥 131 комментариев
#State Management#Архитектура Flutter
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI26 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Какие архитектуры не нравятся?
Как опытный разработчик, я могу выделить несколько архитектурных подходов и паттернов, которые я считаю проблематичными при разработке Flutter приложений. Речь идет не о полном отрицании, а о их ограничениях и проблемах в реальных проектах.
1. MVC (Model-View-Controller)
Проблемы:
- Отсутствие разделения ответственности — Controller часто становится "свалкой" для логики
- Слабая тестируемость — Controller имеет прямые зависимости от View
- Tight coupling — Widget содержит много логики контроллера
- Сложность управления состоянием — Model может быть изменён из разных мест
// Плохо: MVC в Flutter
class LoginController {
BuildContext context; // Сильная связь с UI
void login(String email, String password) {
if (email.isEmpty) {
ScaffoldMessenger.of(context).showSnackBar(...); // Логика UI в контроллере
}
// Тесты сложны, нужно мокировать BuildContext
}
}
2. Сверхусложнённые монолитные классы
Проблемы:
- Нарушение Single Responsibility Principle — один класс делает слишком много
- Плохая переиспользуемость — сложно выделить отдельный функционал
- Сложная тестируемость — много зависимостей для тестирования
- Проблемы с масштабированием — файл растёт бесконечно
// Плохо: монолит
class UserService {
// Авторизация
Future<void> login() {}
Future<void> logout() {}
// API запросы
Future<List<User>> getUsers() {}
Future<void> updateProfile() {}
// Сохранение в БД
Future<void> saveUser() {}
// Кеширование
void clearCache() {}
// Валидация
bool validateEmail() {}
// ... ещё 50 методов
}
3. Полное отсутствие архитектуры (спагетти-код)
Проблемы:
- Невозможность тестирования — код переплетён так, что нельзя выделить части
- Никакой организации — всё находится в одном месте
- Кошмарная поддержка — изменение одной вещи ломает десять других
- Невозможность масштабирования — добавление новых функций становится адом
// Плохо: спагетти
class HomePage extends StatefulWidget {
@override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
List<User> users = [];
bool isLoading = false;
@override
void initState() {
super.initState();
fetchUsers();
}
void fetchUsers() async {
// API запрос
// Обработка ошибок
// Кеширование
// Логирование
// Валидация
// Фильтрация
// Сортировка
// ... всё в одном методе
}
// 1000 строк кода в build()
}
4. Излишний Factory паттерн и Dependency Injection
Проблемы:
- Over-engineering — обилие factory методов для простых объектов
- Сложность чтения кода — сложно найти, где создаётся объект
- Избыточная абстракция — четыре слоя абстракции для простого хранилища
- Производительность — лишние создания объектов и вызовы фабрик
// Плохо: избыточный DI
class ServiceFactory {
static final ServiceFactory _instance = ServiceFactory._internal();
factory ServiceFactory() {
return _instance;
}
ServiceFactory._internal();
RepositoryFactory get repositories => RepositoryFactory();
}
class RepositoryFactory {
UserRepository get user => UserRepositoryImpl(
apiFactory: ApiFactory(),
cachingFactory: CachingFactory(),
);
}
// Использование
final userRepo = ServiceFactory().repositories.user.fetchUser(); // 4 слоя
5. Игнорирование управления состоянием
Проблемы:
- setState() везде — неконтролируемые перестроения виджетов
- Глобальные переменные — состояние разбросано везде
- Race conditions — параллельные изменения состояния
- Невозможность отладки — откуда взялось это состояние?
// Плохо: хаос состояния
global_user_data = null;
class UserWidget extends StatefulWidget {
@override
State<UserWidget> createState() => _UserWidgetState();
}
class _UserWidgetState extends State<UserWidget> {
String? localUser;
void updateUser() {
setState(() {
localUser = global_user_data.name;
// Ещё 50 setState() вызовов
});
}
}
6. Неправильное использование BLoC
Проблемы:
- BLoC для всего — BLoC для логики, которая не требует управления состоянием
- Монолитный BLoC — один BLoC для 10 независимых функций
- Утечки памяти — StreamControllers не закрываются
- Сложность тестирования — BLoC с 20 событиями
// Плохо: неправильный BLoC
class UserBloc extends Bloc<UserEvent, UserState> {
UserBloc() : super(UserInitial()) {
on<FetchUsersEvent>(_onFetchUsers);
on<UpdateUserEvent>(_onUpdateUser);
on<DeleteUserEvent>(_onDeleteUser);
on<FilterUsersEvent>(_onFilterUsers);
on<SortUsersEvent>(_onSortUsers);
on<ExportUsersEvent>(_onExportUsers); // BLoC становится монолитом
}
StreamController<List<User>>? _userController; // Утечка
@override
Future<void> close() {
_userController?.close();
return super.close();
}
}
7. Игнорирование лучших практик SOLID
Проблемы:
- Нарушение DRY — один и тот же код в 10 местах
- Нарушение KISS — простая задача решена за 500 строк
- Нарушение Open/Closed — нужно изменять класс для каждого нового требования
- Нарушение Interface Segregation — огромные интерфейсы с методами, которые не всем нужны
Мой подход к архитектуре
Что я предпочитаю:
- Clean Architecture с чёткими слоями (Presentation, Application, Domain, Infrastructure)
- BLoC / Riverpod для управления состоянием с правильным разделением
- Repository паттерн для абстракции источников данных
- SOLID принципы без over-engineering
- TDD — тесты определяют архитектуру
- Простота — самое простое решение, которое работает
// Хорошо: Clean Architecture
// domain/
class User {
final String id;
final String name;
}
abstract class UserRepository {
Future<User> getUser(String id);
}
// application/
class FetchUserUseCase {
final UserRepository repository;
FetchUserUseCase(this.repository);
Future<User> call(String id) => repository.getUser(id);
}
// presentation/
class UserProvider extends StateNotifier<AsyncValue<User>> {
UserProvider(this.useCase) : super(const AsyncValue.loading());
final FetchUserUseCase useCase;
void fetchUser(String id) async {
state = await AsyncValue.guard(() => useCase(id));
}
}
Заключение
Идеальная архитектура — это та, которая:
- Легко тестируется
- Легко читается и понимается
- Масштабируется без переписывания
- Не требует 5 слоёв абстракции для простой задачи
- Позволяет быстро добавлять новые функции
Нет универсального решения, но есть принципы, которые работают в большинстве случаев.