Compare commits
	
		
			No commits in common. "0219e3d5579411dc317279b29f43ec440ab3d476" and "1f24a767175a8b3fe5820c743d59fb431d37c684" have entirely different histories.
		
	
	
		
			0219e3d557
			...
			1f24a76717
		
	
		
@ -16,159 +16,31 @@
 | 
			
		||||
 * along with this program. If not, see <https://www.gnu.org/licenses/>.
 | 
			
		||||
-->
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# Flutter - Wyatt Architecture
 | 
			
		||||
 | 
			
		||||
<p align="left">
 | 
			
		||||
  <a href="https://git.wyatt-studio.fr/Wyatt-FOSS/wyatt-packages/src/branch/master/packages/wyatt_analysis">
 | 
			
		||||
 | 
			
		||||
    <img src="https://img.shields.io/badge/Style-Wyatt%20Analysis-blue.svg?style=flat-square" alt="Style: Wyatt Analysis" />
 | 
			
		||||
 | 
			
		||||
  </a>
 | 
			
		||||
  <img src="https://img.shields.io/badge/SDK-Flutter-blue?style=flat-square" alt="SDK: Flutter" />
 | 
			
		||||
</p>
 | 
			
		||||
 | 
			
		||||
The Wyatt Architecture for Flutter.
 | 
			
		||||
Architecture for Flutter.
 | 
			
		||||
 | 
			
		||||
Following: <https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html>
 | 
			
		||||
 | 
			
		||||
## Features
 | 
			
		||||
 | 
			
		||||
* Usecase
 | 
			
		||||
* Repository
 | 
			
		||||
* DataSource
 | 
			
		||||
* Entity
 | 
			
		||||
- Usecase
 | 
			
		||||
- Repository
 | 
			
		||||
- DataSource
 | 
			
		||||
- Entity
 | 
			
		||||
 | 
			
		||||
## Getting started
 | 
			
		||||
 | 
			
		||||
<!-- TODO -->
 | 
			
		||||
 | 
			
		||||
## 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<Photo> getPhoto(int id);
 | 
			
		||||
  Future<List<Photo>> getAllPhotos({int? start, int? limit});
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Then you can create your repositories by extenting `BaseRepository` :
 | 
			
		||||
 | 
			
		||||
```dart
 | 
			
		||||
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 by using `UseCase<Parameters, ReturnType>` :
 | 
			
		||||
 | 
			
		||||
```dart
 | 
			
		||||
class RetrieveAllPhoto extends UseCase<QueryParameters, List<Photo>> {
 | 
			
		||||
  final PhotoRepository _photoRepository;
 | 
			
		||||
 | 
			
		||||
  RetrieveAllPhotos(this._photoRepository);
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  FutureResult<List<Photo>> call(QueryParameters params) {
 | 
			
		||||
    final photos = _photoRepository.getAllPhotos(
 | 
			
		||||
      start: params.start,
 | 
			
		||||
      limit: params.limit,
 | 
			
		||||
    );
 | 
			
		||||
    return photos;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
> 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<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:
 | 
			
		||||
 | 
			
		||||
```dart
 | 
			
		||||
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.
 | 
			
		||||
<!-- TODO -->
 | 
			
		||||
@ -14,6 +14,4 @@
 | 
			
		||||
// You should have received a copy of the GNU General Public License
 | 
			
		||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
 | 
			
		||||
 | 
			
		||||
abstract class BaseDataSource {
 | 
			
		||||
  const BaseDataSource();
 | 
			
		||||
}
 | 
			
		||||
abstract class BaseDataSource {}
 | 
			
		||||
 | 
			
		||||
@ -16,6 +16,4 @@
 | 
			
		||||
 | 
			
		||||
import 'package:wyatt_architecture/src/domain/data_sources/base_data_source.dart';
 | 
			
		||||
 | 
			
		||||
abstract class BaseLocalDataSource extends BaseDataSource {
 | 
			
		||||
  const BaseLocalDataSource();
 | 
			
		||||
}
 | 
			
		||||
abstract class BaseLocalDataSource extends BaseDataSource {}
 | 
			
		||||
 | 
			
		||||
@ -16,6 +16,4 @@
 | 
			
		||||
 | 
			
		||||
import 'package:wyatt_architecture/src/domain/data_sources/base_data_source.dart';
 | 
			
		||||
 | 
			
		||||
abstract class BaseRemoteDataSource extends BaseDataSource {
 | 
			
		||||
  const BaseRemoteDataSource();
 | 
			
		||||
}
 | 
			
		||||
abstract class BaseRemoteDataSource extends BaseDataSource {}
 | 
			
		||||
 | 
			
		||||
@ -14,6 +14,4 @@
 | 
			
		||||
// You should have received a copy of the GNU General Public License
 | 
			
		||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
 | 
			
		||||
 | 
			
		||||
abstract class BaseRepository {
 | 
			
		||||
  const BaseRepository();
 | 
			
		||||
}
 | 
			
		||||
class BaseRepository {}
 | 
			
		||||
 | 
			
		||||
@ -18,8 +18,6 @@ import 'package:wyatt_architecture/src/core/exceptions/exceptions.dart';
 | 
			
		||||
import 'package:wyatt_type_utils/wyatt_type_utils.dart';
 | 
			
		||||
 | 
			
		||||
typedef FutureResult<T> = Future<Result<T, AppException>>;
 | 
			
		||||
typedef StreamResult<T> = Stream<Result<T, AppException>>;
 | 
			
		||||
typedef Res<T> = Result<T, AppException>;
 | 
			
		||||
 | 
			
		||||
// ignore: one_member_abstracts
 | 
			
		||||
abstract class UseCase<Parameters, ReturnType> {
 | 
			
		||||
 | 
			
		||||
@ -14,5 +14,4 @@
 | 
			
		||||
// You should have received a copy of the GNU General Public License
 | 
			
		||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
 | 
			
		||||
 | 
			
		||||
export 'no_param.dart';
 | 
			
		||||
export 'usecase.dart';
 | 
			
		||||
 | 
			
		||||
@ -14,4 +14,4 @@
 | 
			
		||||
// You should have received a copy of the GNU General Public License
 | 
			
		||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
 | 
			
		||||
 | 
			
		||||
// Nothing to test as there is no logic in this package.
 | 
			
		||||
// TODO(wyatt): Add some tests
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user