Что такое DTO (Data Transfer Object) в контексте Flutter-приложения?

Ответ

DTO (Data Transfer Object) — это простой объект, предназначенный исключительно для передачи данных между различными слоями приложения или между клиентом и сервером. В моих Flutter-проектах DTO обычно используются для десериализации JSON-ответов от API.

Типичная реализация DTO во Flutter/Dart:

import 'dart:convert';

// DTO для пользовательских данных с API
class UserDto {
  final String id;
  final String email;
  final String firstName;
  final String lastName;
  final DateTime createdAt;

  UserDto({
    required this.id,
    required this.email,
    required this.firstName,
    required this.lastName,
    required this.createdAt,
  });

  // Фабричный конструктор для десериализации JSON
  factory UserDto.fromJson(Map<String, dynamic> json) {
    return UserDto(
      id: json['id'] as String,
      email: json['email'] as String,
      firstName: json['first_name'] as String,
      lastName: json['last_name'] as String,
      createdAt: DateTime.parse(json['created_at'] as String),
    );
  }

  // Метод для сериализации в JSON
  Map<String, dynamic> toJson() => {
    'id': id,
    'email': email,
    'first_name': firstName,
    'last_name': lastName,
    'created_at': createdAt.toIso8601String(),
  };

  // Преобразование DTO в доменную модель
  User toDomain() {
    return User(
      id: UserId(id),
      email: Email(email),
      fullName: '$firstName $lastName',
      registrationDate: createdAt,
    );
  }
}

// Пример использования с http-пакетом
Future<UserDto> fetchUser(String userId) async {
  final response = await http.get(
    Uri.parse('https://api.example.com/users/$userId'),
  );

  if (response.statusCode == 200) {
    final jsonData = jsonDecode(response.body) as Map<String, dynamic>;
    return UserDto.fromJson(jsonData);
  } else {
    throw Exception('Failed to load user');
  }
}

Ключевые характеристики DTO в моей практике:

  • Содержат только данные и методы преобразования
  • Обычно immutable (все поля final)
  • Включают методы fromJson/toJson для работы с API
  • Отделены от бизнес-логики и UI-логики
  • Могут отличаться от доменных моделей структурой полей

Ответ 18+ 🔞

Да ты послушай, что за модная хуйня эта DTO! Это ж просто объект-перевозчик, как грузовик без кабины — только кузов с данными и всё. Нужен, чтобы таскать инфу между сервером и клиентом, или между слоями приложения. В моих Flutter-проектах они в основном для того, чтобы JSON от API разобрать, ёпта.

Вот как это обычно выглядит, смотри:

import 'dart:convert';

// DTO для пользовательских данных с API
class UserDto {
  final String id;
  final String email;
  final String firstName;
  final String lastName;
  final DateTime createdAt;

  UserDto({
    required this.id,
    required this.email,
    required this.firstName,
    required this.lastName,
    required this.createdAt,
  });

  // Фабричный конструктор для десериализации JSON
  factory UserDto.fromJson(Map<String, dynamic> json) {
    return UserDto(
      id: json['id'] as String,
      email: json['email'] as String,
      firstName: json['first_name'] as String,
      lastName: json['last_name'] as String,
      createdAt: DateTime.parse(json['created_at'] as String),
    );
  }

  // Метод для сериализации в JSON
  Map<String, dynamic> toJson() => {
    'id': id,
    'email': email,
    'first_name': firstName,
    'last_name': lastName,
    'created_at': createdAt.toIso8601String(),
  };

  // Преобразование DTO в доменную модель
  User toDomain() {
    return User(
      id: UserId(id),
      email: Email(email),
      fullName: '$firstName $lastName',
      registrationDate: createdAt,
    );
  }
}

// Пример использования с http-пакетом
Future<UserDto> fetchUser(String userId) async {
  final response = await http.get(
    Uri.parse('https://api.example.com/users/$userId'),
  );

  if (response.statusCode == 200) {
    final jsonData = jsonDecode(response.body) as Map<String, dynamic>;
    return UserDto.fromJson(jsonData);
  } else {
    throw Exception('Failed to load user');
  }
}

А теперь по сути, что это за зверь такой:

Во-первых, это просто контейнер. Доверия ебать ноль к нему в плане логики — он только данные держит и методы для их преобразования. Все поля final, то есть неизменяемые, чтоб никто по ходу дела не накосячил.

Во-вторых, у него обязательно есть fromJson и toJson. Это как переводчик с языка API на человеческий, я тебя умоляю. Прилетает какая-то манда с ушами в виде JSON, а DTO её аккуратно разбирает по полочкам.

И главное — он отделён от всей бизнес-логики! Это как будто ты заказываешь пиццу: курьер (DTO) привозит коробку, но готовить и есть её будешь уже сам дома, в своей доменной модели. DTO — не повар, он только доставщик. В рот меня чих-пых, какая простая концепция, а сколько пользы!

И да, структура полей в DTO может вообще не совпадать с твоей внутренней моделью. API может называть поле first_name, а у тебя в приложении оно будет givenName. Да похуй, DTO как раз для этого и нужен — чтобы этот барьер преодолеть. Преобразовал и забыл.