Modelos
Clases
Nombrado de modelos
Los modelos deben incluir el sufijo Model
en su nombre y deben utilizar el mismo nombre base de la entidad correspondiente.
class UserModel { }
class ProductModel { }
A. Contextualización de nombres de clases
Los modelos pueden incluir en su nombre el tipo de objeto que representan. Por ejemplo, Dto
para objetos de transferencia de datos, Response
para objetos de respuesta, Request
para objetos de solicitud, etc; respetando la regla de Nombrado de modelos. Se recomienda mantener un estándar en la contextualización del nombrado de modelos dentro del mismo proyecto o paquete.
class UserDtoModel extends User { }
class CreateUserRequestModel extends CreateUserRequest { }
class UpdateUserResponseModel { }
Extensión de la clase de entidad
Las clases de modelos deben extender de la clase entidad correspondiente.
class UserModel extends User { }
class ProductModel extends Product { }
A. Excepción de extensión
Esta regla no aplica en caso de modelos que no tienen una entidad correspondiente. Por ejemplo, modelos de datos que no representan entidades o modelos de uso limitado a la capa de datos.
Constructores
Constructor fromMap
Los modelos que requieran ser serializados de un mapa deben tener un constructor factory fromMap
.
A. Argumento data
El método fromMap
debe recibir un objeto de tipo Map<E,S>
como argumento obligatorio llamado data
. Generalmente E
es de tipo String
y S
es de tipo dynamic
, pero esto puede variar de un modelo a otro.
class UserModel extends User {
factory UserModel.fromMap(Map<String, dynamic> data) { ... }
}
B. Argumentos extra
El método fromMap
puede recibir argumentos adicionales a data
. El parámetro data
debe ser posicional y el resto de parámetros deben ser de tipo nombrado y pueden ser opcionales u obligatorios.
class UserModel extends User {
factory UserModel.fromMap(Map<String, dynamic> data, {String? extra}) {
...
}
}
C. Variables intermedias en constructores fromMap
Los constructores fromMap
deben utilizar variables intermedias para almacenar los valores de los parámetros del constructor.
class UserModel extends User {
factory UserModel.fromMap(Map<String, dynamic> data) {
// 👇 Variables intermedias
final id = data['id'];
final name = data['name'];
return UserModel(id: id, name: name);
}
}
D. Manejo de valores nulos de parámetros en constructores fromMap
Todos los parámetros provenientes del argumento data
deben evaluar y resolver posibles valores nulos, salvo aquellos valores anulables opcionales, o aquellos que requieran una manejo especial según la necesidad del proyecto.
class UserModel extends User {
factory UserModel.fromMap(Map<String, dynamic> data) {
final id = data['id'] ?? ''; // 👈 Manejo de valor nulo
final name = data['name'] ?? '';
return UserModel(id: id, name: name);
}
}
Atributos
Herencia de atributos de la entidad
Los modelos deben respetar los atributos heredados de la entidad correspondiente. No se debe definir atributos redundantes o repetidos con aquellos presentes en la entidad heredada.
A. Atributos adicionales no redundantes
Los modelos pueden definir atributos adicionales que no estén presentes en la entidad que no sean redundantes, entendiendose por redundantes aquellos atributos con un mismo significado que ya están presentes en la entidad.
class User {
User(String id);
final String id;
}
class UserModel extends User {
UserModel({
required super.id,
required String extra,
});
final String extra;
}
B. Atributos heredados de tipo de dato incompatible
Aquellos atributos del modelo cuyos tipos no coincidan con el tipo de dato de los atributos correspondientes en la entidad deben ser transformados en los constructores del modelo, incluyendo constructores factory.
Por ejemplo:
class UserModel extends User {
UserModel(int id) : super(id.toString());
}
Enumeradores
Los parámetros de objetos con una cantidad finita de valores deben ser representados por enumeradores de Dart.
Los enumeradores que formen parte de la lógica de negocio de la aplicación deben ser declarados en la capa de dominio de la misma. Aquellos enumeradores de uso limitado a la capa de datos deben ser declarados en la capa de datos y no pueden ser utilizados en ninguna otra capa.
Atributo value
de enumeradores
Los modelos de enumeradores deben tener un atributo value
de tipo T
que debe ser igual al valor usado en el servicio externo.
enum UserStatusModel {
active('active'),
inactive('inactive');
const UserStatusModel(this.value);
final String value;
}
Método fromValue
de enumeradores
Aquellos enumeradores cuyos valores provengan de la respuesta de un servicio externo deben tener un método fromValue
que convierta un tipo de dato T
al valor correspondiente del enumerador.
enum UserStatusModel {
active('active'),
inactive('inactive');
const UserStatusModel(this.value);
final String value;
UserStatusModel fromValue(String value) {
return UserStatusModel.values.firstWhere(
(e) => e.value == value,
orElse: () => UserStatusModel.active,
);
}
}
Método toValue
de enumeradores
Los enumeradores cuyos valores deban ser enviados al servicio externo deben tener un método toValue
que convierta el valor del enumerador al valor correspondiente de tipo T
aceptado por el API.
enum UserStatusModel {
active('active'),
inactive('inactive');
const UserStatusModel(this.value);
final String value;
String toValue() => this.value;
}
Se recomienda usar interfaces para forzar la estructura de los enumeradores.