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/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
-->
|
-->
|
||||||
|
|
||||||
|
|
||||||
# Flutter - Wyatt Architecture
|
# Flutter - Wyatt Architecture
|
||||||
|
|
||||||
<p align="left">
|
<p align="left">
|
||||||
<a href="https://git.wyatt-studio.fr/Wyatt-FOSS/wyatt-packages/src/branch/master/packages/wyatt_analysis">
|
<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" />
|
<img src="https://img.shields.io/badge/Style-Wyatt%20Analysis-blue.svg?style=flat-square" alt="Style: Wyatt Analysis" />
|
||||||
|
|
||||||
</a>
|
</a>
|
||||||
<img src="https://img.shields.io/badge/SDK-Flutter-blue?style=flat-square" alt="SDK: Flutter" />
|
<img src="https://img.shields.io/badge/SDK-Flutter-blue?style=flat-square" alt="SDK: Flutter" />
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
The Wyatt Architecture for Flutter.
|
Architecture for Flutter.
|
||||||
|
|
||||||
|
Following: <https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html>
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
* Usecase
|
- Usecase
|
||||||
* Repository
|
- Repository
|
||||||
* DataSource
|
- DataSource
|
||||||
* Entity
|
- Entity
|
||||||
|
|
||||||
|
## Getting started
|
||||||
|
|
||||||
|
<!-- TODO -->
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
### Domain
|
<!-- TODO -->
|
||||||
|
|
||||||
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.
|
|
@ -14,6 +14,4 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
abstract class BaseDataSource {
|
abstract class BaseDataSource {}
|
||||||
const BaseDataSource();
|
|
||||||
}
|
|
||||||
|
@ -16,6 +16,4 @@
|
|||||||
|
|
||||||
import 'package:wyatt_architecture/src/domain/data_sources/base_data_source.dart';
|
import 'package:wyatt_architecture/src/domain/data_sources/base_data_source.dart';
|
||||||
|
|
||||||
abstract class BaseLocalDataSource extends BaseDataSource {
|
abstract class BaseLocalDataSource extends BaseDataSource {}
|
||||||
const BaseLocalDataSource();
|
|
||||||
}
|
|
||||||
|
@ -16,6 +16,4 @@
|
|||||||
|
|
||||||
import 'package:wyatt_architecture/src/domain/data_sources/base_data_source.dart';
|
import 'package:wyatt_architecture/src/domain/data_sources/base_data_source.dart';
|
||||||
|
|
||||||
abstract class BaseRemoteDataSource extends BaseDataSource {
|
abstract class BaseRemoteDataSource extends BaseDataSource {}
|
||||||
const BaseRemoteDataSource();
|
|
||||||
}
|
|
||||||
|
@ -14,6 +14,4 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
abstract class BaseRepository {
|
class BaseRepository {}
|
||||||
const BaseRepository();
|
|
||||||
}
|
|
||||||
|
@ -18,8 +18,6 @@ import 'package:wyatt_architecture/src/core/exceptions/exceptions.dart';
|
|||||||
import 'package:wyatt_type_utils/wyatt_type_utils.dart';
|
import 'package:wyatt_type_utils/wyatt_type_utils.dart';
|
||||||
|
|
||||||
typedef FutureResult<T> = Future<Result<T, AppException>>;
|
typedef FutureResult<T> = Future<Result<T, AppException>>;
|
||||||
typedef StreamResult<T> = Stream<Result<T, AppException>>;
|
|
||||||
typedef Res<T> = Result<T, AppException>;
|
|
||||||
|
|
||||||
// ignore: one_member_abstracts
|
// ignore: one_member_abstracts
|
||||||
abstract class UseCase<Parameters, ReturnType> {
|
abstract class UseCase<Parameters, ReturnType> {
|
||||||
|
@ -14,5 +14,4 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
export 'no_param.dart';
|
|
||||||
export 'usecase.dart';
|
export 'usecase.dart';
|
||||||
|
@ -14,4 +14,4 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
// 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