# Wyatt Architecture

Style: Wyatt Analysis SDK: Dart & Flutter

The Wyatt Architecture for Flutter. Contains useful classes to help you to create a clean architecture following the Wyatt Architecture. (core, data, domain, presentation). ## 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 `BaseDataSource` : ```dart abstract class PhotoRemoteDataSource extends BaseDataSource { 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 RetrieveAllAlbums extends AsyncUseCase> { const RetrieveAllAlbums(this._photoRepository); final PhotoRepository _photoRepository; @override FutureOrResult> execute(QueryParameters params) { final albums = _photoRepository.getAllAlbums( start: params.start, limit: params.limit, ); return albums; } } ``` * No parameter usecase : ```dart class DisplayFavorites extends NoParamsAsyncUseCase> { const DisplayFavorites(this._photoRepository); final PhotoRepository _photoRepository; @override FutureOrResult> execute() { final photos = _photoRepository.getAllPhotosFromFavorites(); return photos; } } ``` You can add alternatve scenarios and check pre/post conditions using our extensions : ```dart class RetrieveAllAlbums extends AsyncUseCase> { const RetrieveAllAlbums(this._photoRepository); final PhotoRepository _photoRepository; @override FutureOrResult> execute(QueryParameters params) { final albums = _photoRepository.getAllAlbums( start: params.start, limit: params.limit, ); return albums; } @override FutureOr onStart(QueryParameters? params) { if (params.start < 0) { throw const ClientException('Invalid start parameter'); } } } ``` You can implement error scenarios overriding `onException` , or check postconditions by overriding `onComplete` . * Stream usecase : ```dart class RetrieveAllAlbums extends AsyncUseCase> { const RetrieveAllAlbums(this._photoRepository); final PhotoRepository _photoRepository; @override FutureOrResult> execute(QueryParameters params) { final albums = _photoRepository.getAllAlbums( start: params.start, limit: params.limit, ); return albums; } @override FutureOrResult> onException(Object e) => Ok([]); } ``` 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.