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: