Compare commits
	
		
			3 Commits
		
	
	
		
			1f24a76717
			...
			0219e3d557
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 0219e3d557 | |||
| 3de7d01173 | |||
| 4183da6259 | 
@ -16,31 +16,159 @@
 | 
				
			|||||||
 * 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>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Architecture for Flutter.
 | 
					The Wyatt 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
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<!-- TODO -->
 | 
					### 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.
 | 
				
			||||||
@ -14,4 +14,6 @@
 | 
				
			|||||||
// 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,4 +16,6 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
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,4 +16,6 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
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,4 +14,6 @@
 | 
				
			|||||||
// 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/>.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class BaseRepository {}
 | 
					abstract class BaseRepository {
 | 
				
			||||||
 | 
					  const BaseRepository();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -18,6 +18,8 @@ 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,4 +14,5 @@
 | 
				
			|||||||
// 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/>.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// TODO(wyatt): Add some tests
 | 
					// Nothing to test as there is no logic in this package.
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user