← Назад к вопросам

Какие архитектуры не нравятся?

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));
  }
}

Заключение

Идеальная архитектура — это та, которая:

  1. Легко тестируется
  2. Легко читается и понимается
  3. Масштабируется без переписывания
  4. Не требует 5 слоёв абстракции для простой задачи
  5. Позволяет быстро добавлять новые функции

Нет универсального решения, но есть принципы, которые работают в большинстве случаев.