# Flutter - Wyatt Architecture

Style: Wyatt Analysis SDK: Flutter

The Wyatt Architecture for Flutter. ## Features - Usecase - Repository - DataSource - Entity ## Usage ### Domain Create your entities by extending `Entity` : ```dart class Photo extends Entity { final int id; final String url; const Photo({ required this.id, required this.url, }); } ``` Then create the data sources by extending `BaseLocalDataSource` or `BaseRemoteDataSource` depending the type of data source. ```dart abstract class PhotoRemoteDataSource extends BaseRemoteDataSource { Future getPhoto(int id); Future> getAllPhotos({int? start, int? limit}); } ``` Then you can create your repositories by extenting `BaseRepository` : ```dart abstract class PhotoRepository extends BaseRepository { FutureResult getPhoto(int id); FutureResult> getAllPhotos({int? start, int? limit}); } ``` > Here the repository is just a proxy of the data sources with result type (to have beautiful error handling). And finaly create your different usecases : Several use cases are supported : - Classic usecase : ```dart class Test extends AsyncUseCase>> { @override FuturOrResult>> call(QueryParameters? params) { final photos = _photoRepository.getAllPhotos( start: params.start, limit: params.limit, ); return photos; } } ``` You can add alternatve scenarios and check pre/post conditions using our extensions : ```dart class SearchPhotos extends AsyncUseCase>> { @override FutureOrResult>> call(QueryParameters? params) { final photos = _photoRepository.getAllPhotos( start: params.start, limit: params.limit, ); return photos; } @override FutureOr onStart(QueryParameters? params) { if(params.start == null || params.limit == null){ throw ClientException('Préconndition non valides'); } } } ``` You can implement error scenarios overriding `onError`, or check postconditions by overriding `onComplete` . - Stream usecase : ```dart class SearchPhotos extends StreamUseCase>> { @override FutureOrResult>>> call(QueryParameters? params) { final photos = _photoRepository.getAllPhotos( start: params.start, limit: params.limit, ); return photos; } } ``` On this case, observers allow you to add alternative scénarios when data changed, overriding `onData` or `onDone`. Please note that to use handlers, call `call` method and not `execute`. > In fact, here we need a new parameter object, so let's create it: ```dart class QueryParameters { final int? start; final int? limit; QueryParameters(this.start, this.limit); } ``` ### Data We start by creating models for photos and list of photos. You can use `freezed`. The `PhotoModel` extends `Photo` with some de/serializer capabilities. And those are used only in data layer. Then implements your data sources: ```dart class PhotoApiDataSourceImpl extends PhotoRemoteDataSource { final MiddlewareClient _client; PhotoApiDataSourceImpl(this._client); @override Future getPhoto(int id) async { final response = await _client.get(Uri.parse('/photos/$id')); final photo = PhotoModel.fromJson(jsonDecode(response.body) as Map); return photo; } @override Future> getAllPhotos({int? start, int? limit}) async { final startQuery = start.isNotNull ? '_start=$start' : ''; final limitQuery = limit.isNotNull ? '_limit=$limit' : ''; final delimiter1 = (startQuery.isNotEmpty || limitQuery.isNotEmpty) ? '?' : ''; final delimiter2 = (startQuery.isNotEmpty && limitQuery.isNotEmpty) ? '&' : ''; final url = '/photos$delimiter1$startQuery$delimiter2$limitQuery'; final response = await _client.get(Uri.parse(url)); final photos = ListPhotoModel.fromJson({'photos': jsonDecode(response.body)}); return photos.photos; } } ``` > 1: Note that here we use `MiddlewareClient` from our http package. > 2: You can create multiple implementations (one real and one mock for example). And implement the repositories: ```dart class PhotoRepositoryImpl extends PhotoRepository { final PhotoRemoteDataSource _photoRemoteDataSource; PhotoRepositoryImpl( this._photoRemoteDataSource, ); @override FutureResult getPhoto(int id) => Result.tryCatchAsync( () => _photoRemoteDataSource.getPhoto(id), (error) => ServerException('Cannot retrieve photo $id.'), ); @override FutureResult> getAllPhotos({int? start, int? limit}) async => Result.tryCatchAsync( () => _photoRemoteDataSource.getAllPhotos(start: start, limit: limit), (error) => ServerException('Cannot retrieve all photos.'), ); } ``` That's all.