1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

わたしのFlutterお勉強用Advent Calendar 2020

Day 18

Flutter TDD Clean Architecture Course その4 - Data Layer

Last updated at Posted at 2020-12-18

全体の再把握

CleanArchitecture.jpg

  • Clean Architectureは図の外側が内側に依存する
    • Domain層は独立して、何にも依存しない
  • コーディングも独立した内側から書き始める
    • Entityがない状態だと、Entityを返すUseCaseを作成できない
  • ただし、UseCaseはより外側のRepositoryを使用する
    • これを解決するためにContract(インターフェイス)を用いる
      • UseCaseはRepositoryのインターフェイスの依存するが実装には依存しない
      • UseCaseはRepositoryの実装がない状態で、Repositoryのインターフェイスを継承したRepositoryのmockを使うことでTDDできる

Data Layer

data-layer-diagram.png

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やフレームワークに依存するものである
    • DataSourceとRepositoryが返すエラーも異なる

      • DataSourceは例外を投げる
      • RepositoryはFailureを使うことでインラインでエラーを返さない
  • DataSourceがデータを受け取るまではコントロールできない外部の世界であり、データを受け取ってからはコントロールできる内部の世界であるため、この違いが生じる。DataSourceはその境界に位置する。

fromJson()とtoJson()

fromJson()

  • fromJson()はMapを受け取りModelを生成するため、factoryとなる

toJson()

  • toJson()は引数を受け取らず、自身のプロパティからMapを生成するインスタンスメソッドとなる
1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?