Question #106MediumFlutter Basics

What are the different types of architecture in flutter and benefits (feature first , layer first)

#flutter#architecture

Answer

Overview

Flutter architecture patterns organize code into structured layers. The two main approaches are feature-first (organized by features) and layer-first (organized by technical layers).


Feature-First Architecture

Code is organized by features/modules (also called "package by feature").

Structure

text
lib/
  features/
    authentication/
      data/
        models/
          user_model.dart
        repositories/
          auth_repository_impl.dart
        datasources/
          auth_remote_datasource.dart
      domain/
        entities/
          user.dart
        repositories/
          auth_repository.dart
        usecases/
          login_usecase.dart
      presentation/
        pages/
          login_page.dart
        widgets/
          login_form.dart
        bloc/
          auth_bloc.dart

    home/
      data/
      domain/
      presentation/

    profile/
      data/
      domain/
      presentation/

  core/
    utils/
    constants/
    widgets/
  main.dart

Benefits

BenefitDescription
High cohesionRelated code stays together
Easy navigationFind everything for a feature in one place
ScalabilityAdd features without affecting others
Team collaborationTeams own specific features
ModularityFeatures can become packages

Example

Authentication feature:

dart
// features/authentication/domain/entities/user.dart
class User {
  final String id;
  final String email;
  User({required this.id, required this.email});
}

// features/authentication/domain/usecases/login_usecase.dart
class LoginUseCase {
  final AuthRepository repository;
  LoginUseCase(this.repository);

  Future<User> call(String email, String password) {
    return repository.login(email, password);
  }
}

// features/authentication/presentation/bloc/auth_bloc.dart
class AuthBloc extends Bloc<AuthEvent, AuthState> {
  final LoginUseCase loginUseCase;

  AuthBloc(this.loginUseCase) : super(AuthInitial()) {
    on<LoginRequested>(_onLoginRequested);
  }

  Future<void> _onLoginRequested(
    LoginRequested event,
    Emitter<AuthState> emit,
  ) async {
    emit(AuthLoading());
    try {
      final user = await loginUseCase(event.email, event.password);
      emit(AuthSuccess(user));
    } catch (e) {
      emit(AuthError(e.toString()));
    }
  }
}

Layer-First Architecture

Code is organized by technical layers (also called "package by layer").

Structure

text
lib/
  data/
    models/
      user_model.dart
      product_model.dart
    repositories/
      auth_repository_impl.dart
      product_repository_impl.dart
    datasources/
      remote/
        auth_api.dart
        product_api.dart
      local/
        database.dart

  domain/
    entities/
      user.dart
      product.dart
    repositories/
      auth_repository.dart
      product_repository.dart
    usecases/
      login_usecase.dart
      fetch_products_usecase.dart

  presentation/
    pages/
      login_page.dart
      home_page.dart
      product_page.dart
    widgets/
      custom_button.dart
      product_card.dart
    bloc/
      auth_bloc.dart
      product_bloc.dart

  core/
    utils/
    constants/
  main.dart

Benefits

BenefitDescription
Clear separationTechnical layers are distinct
Easy to understandFollows traditional MVC/MVVM patterns
Good for small appsSimpler structure for few features
OnboardingNew developers understand layers quickly

Example

Layer-first authentication:

dart
// domain/entities/user.dart
class User {
  final String id;
  final String email;
  User({required this.id, required this.email});
}

// domain/repositories/auth_repository.dart
abstract class AuthRepository {
  Future<User> login(String email, String password);
}

// data/repositories/auth_repository_impl.dart
class AuthRepositoryImpl implements AuthRepository {
  final AuthApi authApi;
  AuthRepositoryImpl(this.authApi);

  
  Future<User> login(String email, String password) async {
    final userModel = await authApi.login(email, password);
    return User(id: userModel.id, email: userModel.email);
  }
}

// presentation/bloc/auth_bloc.dart
class AuthBloc extends Bloc<AuthEvent, AuthState> {
  final AuthRepository authRepository;
  // ... (same as feature-first example)
}

Comparison

AspectFeature-FirstLayer-First
OrganizationBy feature/moduleBy technical layer
Scalability✅ Excellent⚠️ Moderate
Navigation✅ Easy (feature folder)⚠️ Harder (scattered files)
Team collaboration✅ Teams own features⚠️ Conflicts on shared layers
Best forLarge apps, microservicesSmall to medium apps
Onboarding⚠️ Requires explanation✅ Familiar to most devs
Code reuse⚠️ Cross-feature imports✅ Centralized layers

When to Use Which

Use Feature-First

  • ✅ Large apps (10+ features)
  • ✅ Multiple teams working in parallel
  • ✅ Features can be extracted as packages
  • ✅ Microservices-style architecture
  • ✅ Long-term scalability is priority

Example apps:

  • E-commerce (authentication, cart, checkout, orders, profile)
  • Social media (feed, messaging, profile, notifications)

Use Layer-First

  • ✅ Small to medium apps (< 10 features)
  • ✅ Single team
  • ✅ Simple domain logic
  • ✅ Quick prototyping
  • ✅ Learning Flutter architecture

Example apps:

  • Todo app
  • Note-taking app
  • Simple CRUD apps

Hybrid Approach

Combine both for best of both worlds.

text
lib/
  features/
    authentication/       # Feature-first
      data/
      domain/
      presentation/
    home/
      data/
      domain/
      presentation/

  shared/                 # Shared layer-first
    data/
      models/
      repositories/
    presentation/
      widgets/
      theme/

  core/                   # Core utilities
    utils/
    constants/
  main.dart

Clean Architecture (Feature-First)

Most popular approach for large Flutter apps.

Layers

text
Presentation Layer (UI)
       ↓
Domain Layer (Business Logic)
       ↓
Data Layer (API, Database)

Dependency Rule

Outer layers depend on inner layers, never the reverse.

dart
// ✅ Presentation depends on Domain
class AuthBloc {
  final LoginUseCase loginUseCase; // Domain layer
}

// ✅ Data depends on Domain
class AuthRepositoryImpl implements AuthRepository {
  // Implements domain repository interface
}

// ❌ Domain should NOT depend on Data or Presentation

Complete Feature-First Example

features/products/domain/entities/product.dart:

dart
class Product {
  final String id;
  final String name;
  final double price;

  Product({required this.id, required this.name, required this.price});
}

features/products/domain/repositories/product_repository.dart:

dart
abstract class ProductRepository {
  Future<List<Product>> fetchProducts();
}

features/products/data/repositories/product_repository_impl.dart:

dart
class ProductRepositoryImpl implements ProductRepository {
  final ProductApi productApi;
  ProductRepositoryImpl(this.productApi);

  
  Future<List<Product>> fetchProducts() async {
    final models = await productApi.getProducts();
    return models.map((m) => Product(id: m.id, name: m.name, price: m.price)).toList();
  }
}

features/products/presentation/bloc/product_bloc.dart:

dart
class ProductBloc extends Bloc<ProductEvent, ProductState> {
  final ProductRepository productRepository;

  ProductBloc(this.productRepository) : super(ProductInitial()) {
    on<FetchProducts>(_onFetchProducts);
  }

  Future<void> _onFetchProducts(
    FetchProducts event,
    Emitter<ProductState> emit,
  ) async {
    emit(ProductLoading());
    try {
      final products = await productRepository.fetchProducts();
      emit(ProductLoaded(products));
    } catch (e) {
      emit(ProductError(e.toString()));
    }
  }
}

Best Practices

dart
// ✅ Keep features independent
// features/authentication/ should NOT import from features/products/

// ✅ Use shared code for cross-feature utilities
// shared/widgets/custom_button.dart (used by multiple features)

// ✅ Follow dependency rule (Clean Architecture)
// Presentation → Domain → Data

// ❌ Don't mix architectures inconsistently
// Pick one and stick to it throughout the app

Summary

ArchitectureStructureBest For
Feature-FirstBy feature/moduleLarge apps, teams
Layer-FirstBy technical layerSmall apps, solo devs
HybridFeatures + shared layersMedium to large apps
Clean ArchitectureLayered + feature-firstEnterprise apps

Recommendation: Start with layer-first for small apps, migrate to feature-first as the app grows.

Learn more: