# Wyatt Architecture
  
  
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.