Clean Architecture Linter
π°π· νκ΅μ΄ README | πΊπΈ English README
A comprehensive custom lint package that automatically enforces Clean Architecture principles in Flutter/Dart projects. Write code naturally while the linter guides you toward perfect Clean Architecture compliance with real-time feedback and actionable corrections.
β¨ Key Features
- π‘οΈ Automatic Clean Architecture Protection - Write code freely, linter catches violations
- π― 26 Specialized Rules - Comprehensive coverage of all Clean Architecture layers
- π Flutter-Optimized - Built specifically for Flutter development patterns
- π Educational - Learn Clean Architecture through guided corrections
- β‘ Real-time Feedback - Immediate warnings with actionable solutions
- π§ Zero Configuration - Works out of the box with sensible defaults
- π§ͺ Test-Aware - Smart exceptions for test files and development contexts
π Rules Overview (26 Rules)
π Core Clean Architecture Principles (6 rules)
- Layer Dependency - Enforces dependency direction (inward only)
- Domain Purity - Prevents external framework dependencies in domain layer
- Dependency Inversion - Validates abstraction-based dependencies
- Repository Interface - Ensures proper repository abstractions
- Circular Dependency - Prevents circular dependencies between layers
- Boundary Crossing - Validates proper layer boundary crossing
π― Domain Layer Rules (4 rules)
- UseCase No Result Return - UseCases should unwrap Result types
- UseCase Must Convert Failure - UseCases convert Failures to Exceptions
- Exception Naming Convention - Feature prefix for domain exceptions
- Exception Message Localization - Consistent exception messages
πΎ Data Layer Rules (10 rules)
- Model Structure - Freezed models with entity composition
- Model Field Duplication - No duplicate entity fields in models
- Model Conversion Methods - Required
toEntity()
andfromEntity()
- DataSource Abstraction - Abstract interfaces for data sources
- DataSource No Result Return - DataSources throw exceptions
- Repository Implementation - RepositoryImpl must implement domain interface
- Repository Must Return Result - Repositories wrap results in Result type
- Repository No Throw - Repositories convert exceptions to Result
- DataSource Exception Types - Use defined data layer exceptions only
- Failure Naming Convention - Feature prefix for Failure classes
π¨ Presentation Layer Rules (6 rules)
- No Presentation Models - Use Freezed State instead of ViewModels
- Extension Location - Extensions in same file as the class
- Freezed Usage - Use Freezed instead of Equatable
- Riverpod Generator - Use
@riverpod
annotation - Presentation No Data Exceptions - Use domain exceptions only
- Presentation Use AsyncValue - Use AsyncValue for error handling
π§ͺ Optional: Test Coverage Rule
Test Coverage - Enforces test files for UseCases, Repositories, DataSources, and Notifiers (disabled by default)
π Implementation Guide: See CLEAN_ARCHITECTURE_GUIDE.md for detailed patterns and examples.
π Quick Start
π Requirements
- Dart SDK: 3.6.0+
- Flutter: 3.0+ (optional, for Flutter projects)
1. Add to your project
# pubspec.yaml
dev_dependencies:
clean_architecture_linter: ^1.0.0
custom_lint: ^0.7.6
2. Enable custom lint
# analysis_options.yaml
analyzer:
plugins:
- custom_lint
exclude:
- test/**
- "**/*.test.dart" # Exclude test files
- "**/*.g.dart" # Exclude generated files
- "**/*.freezed.dart" # Exclude Freezed files
- "**/*.mocks.dart" # Exclude mock files
3. Run the linter
dart pub get
dart pub custom_lint
That's it! The linter will now automatically enforce Clean Architecture principles in your codebase.
ποΈ Configuration
Optional: Test Coverage
The clean_architecture_linter_require_test
rule is disabled by default. Enable it to enforce test files for critical components:
# analysis_options.yaml
custom_lint:
rules:
- clean_architecture_linter_require_test: true
check_usecases: true # Require tests for UseCases
check_repositories: true # Require tests for Repositories
check_datasources: true # Require tests for DataSources
check_notifiers: true # Require tests for Notifiers
π¦ Usage
Folder Structure
Organize your Flutter project following Clean Architecture:
lib/
βββ domain/
β βββ entities/
β βββ repositories/
β βββ usecases/
βββ data/
β βββ datasources/
β βββ models/
β βββ repositories/
βββ presentation/
βββ providers/
βββ widgets/
βββ pages/
Running the Linter
# Activate custom_lint if not already done
dart pub global activate custom_lint
# Run the linter
dart pub custom_lint
IDE Integration
The linter works automatically in:
- VS Code with the Dart/Flutter extensions
- IntelliJ IDEA / Android Studio with Flutter plugin
π Examples
β Good Examples
Domain Entity (Immutable)
// lib/domain/entities/user_entity.dart
class UserEntity {
final String id;
final String name;
final String email;
const UserEntity({
required this.id,
required this.name,
required this.email,
});
bool isValidEmail() {
return email.contains('@');
}
}
Repository Interface
// lib/domain/repositories/user_repository.dart
abstract class UserRepository {
Future<UserEntity> getUser(String id);
Future<void> saveUser(UserEntity user);
}
UseCase with Single Responsibility
// lib/domain/usecases/get_user_usecase.dart
class GetUserUseCase {
final UserRepository repository;
GetUserUseCase(this.repository);
Future<UserEntity> call(String userId) {
return repository.getUser(userId);
}
}
β Bad Examples (Will be flagged)
Mutable Domain Entity
// β This will be flagged by entity_immutability
class UserEntity {
String name; // Non-final field
void setName(String newName) { // Setter in entity
name = newName;
}
}
Domain Layer with External Dependencies
// β This will be flagged by domain_purity
import 'package:http/http.dart'; // External framework import
class UserEntity {
final String name;
}
UI with Direct Business Logic
// β This will be flagged by business_logic_isolation
class UserWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
// Business logic in UI layer - WRONG!
final isValid = email.contains('@') && email.length > 5;
return Text(isValid ? 'Valid' : 'Invalid');
}
}
Repository Throwing Exceptions
// β This will be flagged by avoid_exception_throwing_in_repository
class UserRepositoryImpl implements UserRepository {
@override
Future<UserEntity> getUser(String id) async {
if (id.isEmpty) {
throw ArgumentError('ID cannot be empty'); // Should return Result instead
}
// ...
}
}
Layer Dependency Violation
// β This will be flagged by avoid_layer_dependency_violation
// In domain layer file:
import 'package:myapp/data/models/user_model.dart'; // Domain importing Data!
class UserEntity extends UserModel { // Wrong dependency direction
// ...
}
Missing Exception Prefix
// β This will be flagged by ensure_exception_prefix
class NetworkException extends Exception { // Should be UserNetworkException
// ...
}
π Common Patterns
Proper Error Handling with Result Type
// β
Good: Using Result pattern
sealed class Result<T, E> {}
class Success<T, E> extends Result<T, E> {
final T value;
Success(this.value);
}
class Failure<T, E> extends Result<T, E> {
final E error;
Failure(this.error);
}
// Repository implementation
class UserRepositoryImpl implements UserRepository {
@override
Future<Result<UserEntity, UserException>> getUser(String id) async {
try {
final userData = await dataSource.getUser(id);
return Success(userData.toEntity());
} catch (e) {
return Failure(UserDataException(e.toString()));
}
}
}
Proper Exception Naming
// β
Good: Proper exception prefixes
class UserNetworkException extends Exception {
final String message;
UserNetworkException(this.message);
}
class UserValidationException extends Exception {
final String field;
UserValidationException(this.field);
}
For more detailed examples and explanations, see our comprehensive Examples Guide.
π οΈ Development
Project Structure
clean_architecture_linter/
βββ lib/
β βββ src/
β β βββ rules/
β β βββ domain_rules/
β β βββ data_rules/
β β βββ presentation_rules/
β βββ clean_architecture_linter.dart
βββ example/
βββ test/
βββ README.md
Contributing
- Fork the repository
- Create a feature branch
- Add tests for new rules
- Format your code:
dart format --line-length=120 .
- Ensure all tests pass
- Submit a pull request
See CONTRIBUTING.md for detailed guidelines.
π License
This project is licensed under the MIT License - see the LICENSE file for details.
π Support
- β Star this repository if it helped you!
- π Report bugs
- π‘ Request features
- π Read the documentation
π― Roadmap
Made with β€οΈ for the Flutter community
Libraries
- clean_architecture_linter
- A focused custom lint package that enforces Clean Architecture principles in Flutter projects.