clean_architecture_linter 1.0.1
clean_architecture_linter: ^1.0.1 copied to clipboard
A comprehensive custom lint package that automatically enforces Clean Architecture principles in Flutter projects with specialized rules.
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
- π― 27 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 (27 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 (5 rules) #
- Entity Business Logic - Ensures entities contain business rules (not anemic)
- 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. final user = UserRepository().getUser('123'); return Text(user.name); } }
## π οΈ Development
### Project Structure
clean_architecture_linter/ βββ lib/ β βββ src/ β β βββ rules/ β β βββ domain_rules/ β β βββ data_rules/ β β βββ presentation_rules/ β βββ clean_architecture_linter.dart βββ example/ βββ test/ βββ README.md
### Contributing
1. Fork the repository
2. Create a feature branch
3. Add tests for new rules
4. Format your code: `dart format --line-length=120 .`
5. Ensure all tests pass
6. Submit a pull request
See [CONTRIBUTING.md](CONTRIBUTING.md) for detailed guidelines.
## π License
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
## π Support
- β Star this repository if it helped you!
- π [Report bugs](https://github.com/ittae/clean_architecture_linter/issues)
- π‘ [Request features](https://github.com/ittae/clean_architecture_linter/issues)
- π [Read the documentation](https://github.com/ittae/clean_architecture_linter)
## π― Roadmap
- [ ] Configuration system for custom naming patterns
- [ ] Support for multiple state management solutions
- [ ] Integration with CI/CD workflows
- [ ] Custom rule creation guide
- [ ] Performance optimizations
---
**Made with β€οΈ for the Flutter community**