Notice: This page requires JavaScript to function properly.
Please enable JavaScript in your browser settings or update your browser.
Learn DTOs and Domain Models | Clean Architecture for API Apps
Flutter REST API Integration

bookDTOs and Domain Models

When building Flutter apps that interact with REST APIs, you often receive data in the form of raw JSON. However, letting your UI code depend directly on this JSON, or even on data transfer objects (DTOs) that mirror the API, is risky and limiting. Instead, you should introduce two layers: DTOs and domain models.

What is a DTO?
expand arrow

A DTO (Data Transfer Object) is a simple class that represents the data structure exactly as it comes from the API. It is designed for serialization and deserialization, making it easy to convert between Dart objects and JSON. DTOs are often tightly coupled to the API contract and may include nullable fields, naming conventions, or types that match the backend.

What is a Domain Model?
expand arrow

A domain model represents the core concepts and business logic of your app, independent of how the data is fetched or stored. Domain models are what your UI and business logic should use. They are shaped according to your app’s needs, not the backend’s structure. By mapping DTOs to domain models, you shield your app from backend changes and keep your UI logic clean and focused.

main.dart

main.dart

copy
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869
// DTO representing the API response for a user class UserDto { final int id; final String? first_name; final String? last_name; final String? email; UserDto({ required this.id, this.first_name, this.last_name, this.email, }); factory UserDto.fromJson(Map<String, dynamic> json) { return UserDto( id: json['id'] as int, first_name: json['first_name'] as String?, last_name: json['last_name'] as String?, email: json['email'] as String?, ); } } // Domain model representing a user in the app class User { final int id; final String fullName; final String email; User({ required this.id, required this.fullName, required this.email, }); } // Mapper function to convert a UserDto to a User domain model User userFromDto(UserDto dto) { final firstName = dto.first_name ?? ''; final lastName = dto.last_name ?? ''; return User( id: dto.id, fullName: (firstName + ' ' + lastName).trim(), email: dto.email ?? '', ); } // Example usage void main() { // Simulated API response final json = { 'id': 42, 'first_name': 'Ada', 'last_name': 'Lovelace', 'email': 'ada@example.com', }; // Parse DTO from JSON final userDto = UserDto.fromJson(json); // Map DTO to domain model final user = userFromDto(userDto); print('User domain model:'); print('ID: ${user.id}'); print('Full Name: ${user.fullName}'); print('Email: ${user.email}'); }

By separating DTOs from domain models, you gain important flexibility. Your UI and business logic can work with clean, well-defined models that fit your app's needs, rather than being forced to match the backend's data structure. If the API changes, you only need to update the DTO and mapping layer, not your entire app.

Note
Note

This also lets you add computed fields, validation, or transformations in the mapping step.

question mark

Why should UI code use domain models instead of raw JSON or DTOs?

Select the correct answer

Everything was clear?

How can we improve it?

Thanks for your feedback!

SectionΒ 3. ChapterΒ 2

Ask AI

expand

Ask AI

ChatGPT

Ask anything or try one of the suggested questions to begin our chat

Suggested prompts:

Can you explain the difference between DTOs and domain models?

How do I implement a mapping layer between DTOs and domain models in Flutter?

What are some best practices for managing DTOs and domain models in a Flutter project?

bookDTOs and Domain Models

Swipe to show menu

When building Flutter apps that interact with REST APIs, you often receive data in the form of raw JSON. However, letting your UI code depend directly on this JSON, or even on data transfer objects (DTOs) that mirror the API, is risky and limiting. Instead, you should introduce two layers: DTOs and domain models.

What is a DTO?
expand arrow

A DTO (Data Transfer Object) is a simple class that represents the data structure exactly as it comes from the API. It is designed for serialization and deserialization, making it easy to convert between Dart objects and JSON. DTOs are often tightly coupled to the API contract and may include nullable fields, naming conventions, or types that match the backend.

What is a Domain Model?
expand arrow

A domain model represents the core concepts and business logic of your app, independent of how the data is fetched or stored. Domain models are what your UI and business logic should use. They are shaped according to your app’s needs, not the backend’s structure. By mapping DTOs to domain models, you shield your app from backend changes and keep your UI logic clean and focused.

main.dart

main.dart

copy
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869
// DTO representing the API response for a user class UserDto { final int id; final String? first_name; final String? last_name; final String? email; UserDto({ required this.id, this.first_name, this.last_name, this.email, }); factory UserDto.fromJson(Map<String, dynamic> json) { return UserDto( id: json['id'] as int, first_name: json['first_name'] as String?, last_name: json['last_name'] as String?, email: json['email'] as String?, ); } } // Domain model representing a user in the app class User { final int id; final String fullName; final String email; User({ required this.id, required this.fullName, required this.email, }); } // Mapper function to convert a UserDto to a User domain model User userFromDto(UserDto dto) { final firstName = dto.first_name ?? ''; final lastName = dto.last_name ?? ''; return User( id: dto.id, fullName: (firstName + ' ' + lastName).trim(), email: dto.email ?? '', ); } // Example usage void main() { // Simulated API response final json = { 'id': 42, 'first_name': 'Ada', 'last_name': 'Lovelace', 'email': 'ada@example.com', }; // Parse DTO from JSON final userDto = UserDto.fromJson(json); // Map DTO to domain model final user = userFromDto(userDto); print('User domain model:'); print('ID: ${user.id}'); print('Full Name: ${user.fullName}'); print('Email: ${user.email}'); }

By separating DTOs from domain models, you gain important flexibility. Your UI and business logic can work with clean, well-defined models that fit your app's needs, rather than being forced to match the backend's data structure. If the API changes, you only need to update the DTO and mapping layer, not your entire app.

Note
Note

This also lets you add computed fields, validation, or transformations in the mapping step.

question mark

Why should UI code use domain models instead of raw JSON or DTOs?

Select the correct answer

Everything was clear?

How can we improve it?

Thanks for your feedback!

SectionΒ 3. ChapterΒ 2
some-alt