wyatt-packages/packages/wyatt_architecture
Malo Léon d5ef2124e3 chore(release): publish packages
- wyatt_analysis@2.7.0
 - wyatt_architecture@0.2.0+2
 - wyatt_authentication_bloc@0.6.0
 - wyatt_cloud_messaging_bloc_base@0.1.1+4
 - wyatt_continuous_deployment@1.1.0
 - wyatt_form_bloc@0.2.0+5
 - wyatt_go_router@1.0.1
 - wyatt_http_client@2.0.2
 - wyatt_type_utils@0.1.0
 - wyatt_ui_components@0.4.0
 - wyatt_ui_layout@0.1.3
 - wyatt_crud_bloc_firestore@0.2.0+1
 - wyatt_crud_bloc@0.2.0+1
 - wyatt_i18n@2.0.3
 - wyatt_cloud_messaging_bloc_firebase@0.1.0+4
 - wyatt_bloc_layout@0.1.2+6
 - wyatt_ui_kit@3.2.5
2025-05-21 16:06:29 +02:00
..
2025-05-21 15:57:39 +02:00
2023-04-13 23:29:26 +02:00
2025-05-21 16:06:29 +02:00
2025-05-21 16:06:29 +02:00

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 :

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 :

abstract class PhotoRemoteDataSource extends BaseDataSource {
  Future<Photo> getPhoto(int id);
  Future<List<Photo>> getAllPhotos({int? start, int? limit});
}

Then you can create your repositories by extenting BaseRepository :

abstract class PhotoRepository extends BaseRepository {
  FutureResult<Photo> getPhoto(int id);
  FutureResult<List<Photo>> 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 :
class RetrieveAllAlbums extends AsyncUseCase<QueryParameters, List<Album>> {
  const RetrieveAllAlbums(this._photoRepository);
  final PhotoRepository _photoRepository;

  @override
  FutureOrResult<List<Album>> execute(QueryParameters params) {
    final albums = _photoRepository.getAllAlbums(
      start: params.start,
      limit: params.limit,
    );
    return albums;
  }
}
  • No parameter usecase :
class DisplayFavorites extends NoParamsAsyncUseCase<List<Photo>> {
  const DisplayFavorites(this._photoRepository);
  final PhotoRepository _photoRepository;

  @override
  FutureOrResult<List<Photo>> execute() {
    final photos = _photoRepository.getAllPhotosFromFavorites();
    return photos;
  }
}

You can add alternatve scenarios and check pre/post conditions using our extensions :

class RetrieveAllAlbums extends AsyncUseCase<QueryParameters, List<Album>> {
  const RetrieveAllAlbums(this._photoRepository);
  final PhotoRepository _photoRepository;

  @override
  FutureOrResult<List<Album>> execute(QueryParameters params) {
    final albums = _photoRepository.getAllAlbums(
      start: params.start,
      limit: params.limit,
    );
    return albums;
  }

  @override
  FutureOr<void> 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 :
class RetrieveAllAlbums extends AsyncUseCase<QueryParameters, List<Album>> {
  const RetrieveAllAlbums(this._photoRepository);
  final PhotoRepository _photoRepository;

  @override
  FutureOrResult<List<Album>> execute(QueryParameters params) {
    final albums = _photoRepository.getAllAlbums(
      start: params.start,
      limit: params.limit,
    );
    return albums;
  }

  @override
  FutureOrResult<List<Album>> 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:

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:

class PhotoApiDataSourceImpl extends PhotoRemoteDataSource {
  final MiddlewareClient _client;

  PhotoApiDataSourceImpl(this._client);

  @override
  Future<Photo> getPhoto(int id) async {
    final response = await _client.get(Uri.parse('/photos/$id'));
    final photo =
        PhotoModel.fromJson(jsonDecode(response.body) as Map<String, Object?>);
    return photo;
  }

  @override
  Future<List<Photo>> 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:

class PhotoRepositoryImpl extends PhotoRepository {
  final PhotoRemoteDataSource _photoRemoteDataSource;

  PhotoRepositoryImpl(
    this._photoRemoteDataSource,
  );

  @override
  FutureResult<Photo> getPhoto(int id) => Result.tryCatchAsync(
        () => _photoRemoteDataSource.getPhoto(id),
        (error) => ServerException('Cannot retrieve photo $id.'),
      );

  @override
  FutureResult<List<Photo>> getAllPhotos({int? start, int? limit}) async =>
      Result.tryCatchAsync(
        () => _photoRemoteDataSource.getAllPhotos(start: start, limit: limit),
        (error) => ServerException('Cannot retrieve all photos.'),
      );
}

That's all.