- こちらのチュートリアルの雑なまとめをします。
- 完成コードはチュートリアルにあるのでご参照ください。
- チュートリアルを読むのが早いと思いますが、概要をこちらにまとめたのでこちらもご参照ください。
全体の再把握
- Clean Architectureは図の外側が内側に依存する
- Domain層は独立して、何にも依存しない
- コーディングも独立した内側から書き始める
- Entityがない状態だと、Entityを返すUseCaseを作成できない
- ただし、UseCaseはより外側のRepositoryを使用する
- これを解決するためにContract(インターフェイス)を用いる
- UseCaseはRepositoryのインターフェイスの依存するが実装には依存しない
- UseCaseはRepositoryの実装がない状態で、Repositoryのインターフェイスを継承したRepositoryのmockを使うことでTDDできる
- これを解決するためにContract(インターフェイス)を用いる
Data Layer
Data LayerはModelを扱う
- DataSourceはRepositoryが返す型は似ているが、以下の点で異なる
- RepositoryはFailureとしてエラーをインラインで渡したが、DataSourceはExceptionsを投げる
- RepositoryはNumberTrivia Entityを返したが、DataSourceはNumberTrivia Modelを返す
- ModelはEntityにJSONを扱うメソッドを追加したものになる
ModelをTDDする
- ModelがEntityの子クラスであるテスト
number_trivia_model_test.dart
expect(tNumberTriviaModel, isA<NumberTrivia>());
ModelのJSONを扱うメソッドのTDD
- fixtureを使う
- testのデータを作るもの
Fixture
trivia.json
{
"text": "418 is the error code for \"I'm a teapot\" in the Hyper Text Coffee Pot Control Protocol.",
"number": 418,
"found": true,
"type": "trivia"
}
trivia_double.json
{
"text": "Test Text",
"number": 1.0,
"found": true,
"type": "trivia"
}
fixture_reader
- testファイルでfixtureを読み込むために、fixture_readerを用いる
fixture_reader.dart
import 'dart:io';
String fixture(String name) => File('test/fixtures/$name').readAsStringSync();
fromJson
- 整数fixtureと少数fixtureを呼ぶ場合のテストを書き、テストが成功するように実装する
- 整数完成後、少数のテスト失敗→実装修正を書く
number_trivia_model_test.dart
void main() {
final tNumberTriviaModel = NumberTriviaModel(number: 1, text: 'Test Text');
...
group('fromJson', () {
test('should return a valid model when the JSON number is an integer', () async {
// arrange
final Map<String, dynamic> jsonMap = json.decode(fixture('trivia.json'));
// act
final result = NumberTriviaModel.fromJson(jsonMap);
// assert
expect(result, tNumberTriviaModel);
});
test(
'should return a valid model when the JSON number is regarded as a double',
() async {
// arrange
final Map<String, dynamic> jsonMap = json.decode(fixture('trivia_double.json'));
// act
final result = NumberTriviaModel.fromJson(jsonMap);
// assert
expect(result, tNumberTriviaModel);
},
);
});
}}
number_trivia_model.dart
class NumberTriviaModel extends NumberTrivia {
...
factory NumberTriviaModel.fromJson(Map<String, dynamic> json) {
return NumberTriviaModel(
text: json['text'],
number: (json['number'] as num).toInt(),
);
}
}
toJson
- fromJsonと同様
number_trivia_model_test.dart
...
final tNumberTriviaModel = NumberTriviaModel(number: 1, text: 'Test Text');
...
group('toJson', () {
test(
'should return a JSON map containing the proper data',
() async {
// act
final result = tNumberTriviaModel.toJson();
// assert
final expectedJsonMap = {
"text": "Test Text",
"number": 1,
};
expect(result, expectedJsonMap);
},
);
});
...
number_trivia_model.dart
class NumberTriviaModel extends NumberTrivia {
...
Map<String, dynamic> toJson() {
return {
'text': text,
'number': number,
};
}
}
学んだこと
DataSourceとRepositoryの返す型は似ているが大きく異る
-
DataSourceが返す型とRepositoryが返す型は似ているが大きく異なることを本チュートリアルは強調する
-
DataSourceが返す型はModelであり、Repositoryが返す型はEntityである。
- 最も内側のEntityと、外側にあるModelはコードとふるまいは似ているが、CleanArchitecture上は別物として扱っている
- コード上異なるのはJSONが扱えるところだけである
- これは普遍的なビジネスロジックではなく外部のAPIやフレームワークに依存するものである
- 最も内側のEntityと、外側にあるModelはコードとふるまいは似ているが、CleanArchitecture上は別物として扱っている
-
DataSourceとRepositoryが返すエラーも異なる
- DataSourceは例外を投げる
- RepositoryはFailureを使うことでインラインでエラーを返さない
-
- DataSourceがデータを受け取るまではコントロールできない外部の世界であり、データを受け取ってからはコントロールできる内部の世界であるため、この違いが生じる。DataSourceはその境界に位置する。
fromJson()とtoJson()
fromJson()
- fromJson()はMapを受け取りModelを生成するため、factoryとなる
toJson()
- toJson()は引数を受け取らず、自身のプロパティからMapを生成するインスタンスメソッドとなる