Introduzione alla Clean Architecture
Nel video di oggi esploriamo uno dei pattern architetturali più importanti per lo sviluppo mobile: la Clean Architecture. Questo approccio, ideato da Robert C. Martin (Uncle Bob), ci permette di creare applicazioni più mantenibili, testabili e indipendenti dai framework.
Perché Clean Architecture?
La Clean Architecture risolve molti problemi comuni nello sviluppo mobile:
- Separazione delle responsabilità: Ogni layer ha un compito specifico
- Testabilità: Il codice business è completamente isolato
- Indipendenza dal framework: Il core della logica non dipende da Flutter
- Scalabilità: Facilità nell’aggiungere nuove funzionalità
I Layer della Clean Architecture
1. Presentation Layer
Il layer di presentazione contiene tutto ciò che riguarda l’interfaccia utente:
class UserProfileBloc extends Bloc<UserProfileEvent, UserProfileState> {
final GetUserUseCase getUserUseCase;
UserProfileBloc({required this.getUserUseCase}) : super(UserProfileInitial()) {
on<LoadUserProfile>(_onLoadUserProfile);
}
Future<void> _onLoadUserProfile(
LoadUserProfile event,
Emitter<UserProfileState> emit,
) async {
emit(UserProfileLoading());
final result = await getUserUseCase(UserParams(id: event.userId));
result.fold(
(failure) => emit(UserProfileError(failure.message)),
(user) => emit(UserProfileLoaded(user)),
);
}
}2. Domain Layer
Il cuore della nostra applicazione, completamente indipendente:
abstract class UserRepository {
Future<Either<Failure, User>> getUser(String id);
Future<Either<Failure, List<User>>> getUsers();
}
class GetUserUseCase {
final UserRepository repository;
GetUserUseCase(this.repository);
Future<Either<Failure, User>> call(UserParams params) {
return repository.getUser(params.id);
}
}3. Data Layer
Gestisce l’accesso ai dati esterni:
class UserRepositoryImpl implements UserRepository {
final UserRemoteDataSource remoteDataSource;
final UserLocalDataSource localDataSource;
final NetworkInfo networkInfo;
UserRepositoryImpl({
required this.remoteDataSource,
required this.localDataSource,
required this.networkInfo,
});
@override
Future<Either<Failure, User>> getUser(String id) async {
if (await networkInfo.isConnected) {
try {
final user = await remoteDataSource.getUser(id);
await localDataSource.cacheUser(user);
return Right(user.toDomain());
} catch (e) {
return Left(ServerFailure());
}
} else {
try {
final user = await localDataSource.getUser(id);
return Right(user.toDomain());
} catch (e) {
return Left(CacheFailure());
}
}
}
}Vantaggi Pratici
Testing Semplificato
Con questa architettura, testare diventa incredibilmente semplice:
void main() {
group('GetUserUseCase', () {
late GetUserUseCase useCase;
late MockUserRepository mockRepository;
setUp(() {
mockRepository = MockUserRepository();
useCase = GetUserUseCase(mockRepository);
});
test('should return user when repository call is successful', () async {
// arrange
const testUser = User(id: '1', name: 'Test User');
when(() => mockRepository.getUser(any()))
.thenAnswer((_) async => const Right(testUser));
// act
final result = await useCase(const UserParams(id: '1'));
// assert
expect(result, const Right(testUser));
});
});
}Dependency Injection
Utilizziamo get_it per gestire le dipendenze:
final sl = GetIt.instance;
Future<void> init() async {
// Use cases
sl.registerLazySingleton(() => GetUserUseCase(sl()));
// Repository
sl.registerLazySingleton<UserRepository>(
() => UserRepositoryImpl(
remoteDataSource: sl(),
localDataSource: sl(),
networkInfo: sl(),
),
);
// Data sources
sl.registerLazySingleton<UserRemoteDataSource>(
() => UserRemoteDataSourceImpl(client: sl()),
);
}Conclusioni
La Clean Architecture non è solo un pattern teorico, ma uno strumento pratico che migliora drasticamente la qualità del codice. Nel video approfondiamo ogni aspetto con esempi pratici e casi d’uso reali.
Prossimi Passi
- Guarda il video completo per vedere l’implementazione in azione
- Scarica il codice esempio dal repository
- Prova ad applicare questi concetti al tuo prossimo progetto Flutter
Hai domande o dubbi? Lascia un commento nel video YouTube o contattami sui social!