Repositorios
Clases
Nombrado de repositorios
El nombre de la clase repositorio debe representar el módulo o el conjunto de datos que gestiona y debe terminar con el sufijo Repository
.
class UserRepository
class ReportRepository
class PortfolioRepository
Extensión de la interfaz del repositorio (capa de dominio)
La clase repositorio de la capa de datos debe extender de la interfaz repositorio de la capa dominio.
class UserRepository extends IUserRepository {}
class ReportRepository IReportRepository {}
class PortfolioRepository IPortfolioRepository {}
Constructores
Inyección de dependencias
El repositorio debe recibir las variables y dependencias como parámetros delegados y asignarlos a los atributos privados respectivos de la clase en el constructor.
class UserRepository extends IUserRepository {
// Se pide por parametros y se instancia a la variable api en el archivo
UserRepository(UserApi api) : _api = api;
final UserApi _api;
}
Atributos
Declarar variables públicas y privadas
A. Atributos privados
Los atributos de uso interno de la clase, como dependencias y variables auxiliares, deben ser atributos privados.
B. Atributos públicos
Los atributos públicos que sean parte de la firma de la interfaz del repositorio deben declararse como getters de atributos privados.
abstract class IUserRepository {
List<Country> countries; // Atributo parte de la firma de la interfaz
}
class UserRepository extends IUserRepository {
List<Country> _countries = <Country>[];
List<Country> get countries => _countries; // Implementación del atributo de la interfaz por medio de un getter.
}
Streams
Los Streams
deben crearse a partir de un StreamController
que controle el flujo de información del Stream desde y hacia el repositorio.
Declaración
A. StreamController
de tipo BehaviorSubject
El StreamController
debe declararse como una variable privada. El valor asignado debe ser de tipo BehaviorSubject
del paquete package:rxdart.
B. Atributo de tipo Stream<T>
El atributo de tipo Stream<T>
debe ser un getter del stream de la variable StreamController
de la clase.
class UserRepository extends IUserRepository {
UserRepository(UserApi api) : _api = api;
final UserApi _api;
final _currentUserController = BehaviorSubject<User>();
/// Stream que emite el estado actual y
/// los suscriptores pueden escuchar este stream para recibir actualizaciones.
Stream<User> get currentUserStream => _currentUserController.stream;
}
Logger
Las funciones en los repositorios deben realizar un control de ejecución de las llamadas con la función log
de la librería dart:developer
.
Rastreo de llamadas
A. Eventos de ejecución
Al momento de iniciar una llamada deben registrarse al menos tres eventos de la ejecución: Al iniciar, al finalizar con éxito o al finalizar con un error.
B. Emojis para identificar los eventos de ejecución
Cada evento de ejecución debe incluir un emoji en el log de la llamada. Los emojis deben ser los siguientes:
- 📡 Inicio de la llamada
- ✅ Llamada exitosa
- ❌ Error en la llamada
C. Variable _source
Los repositorios deben declarar la variable privada _source
de tipo estática y constante con el nombre de la clase para identificar el origen de la llamada de la función log
.
D. Declaración de parámetros error
y stackTrace
en el log de bloque catch
Se deben declarar los parámetros error
y stackTrace
del método log
en el bloque catch
de la función.
class PostRepository extends IPostRepository {
PostRepository({required PostApi api}) : _api = api;
final PostApi _api;
static const String _source = 'PostRepository';
Future<void> deleteComment({
required String userId,
required String commentId,
}) async {
try {
log(
'📡 Delete comment $commentId',
name: '$_source.deleteComment',
);
await _api.deleteCommentPost(
itemId: commentId,
ownerId: userId,
);
log(
'✅ Success delete comment $commentId',
name: '$_source.deleteComment',
);
} catch (e, s) {
log(
'❌ Delete comment $commentId',
name: '$_source.deleteComment',
error: e,
stackTrace: s,
);
rethrow;
}
}
}
Métodos
Flujo de ejecución (try/catch)
Las funciones de los repositorios deben incluir un control de flujo implementado bajo un try/catch
y del retorno de excepciones segun sea el caso. Como el siguiente ejemplo:
class ReportsRepository extends IReportsRepository {
ReportsRepository(ReportsApi api) : api = api;
final _source = 'ReportsRepository';
final ReportsApi api;
Future<void> createReport({
required Report report,
required String locale,
}) async {
try {
log(
'📡 Creating report',
name: '$_source.createReport',
);
await api.createReport(
report: ReportModel.fromEntity(report),
locale: locale,
);
log(
'✅ Report created',
name: '$_source.createReport',
);
} on ResponseException catch (e) {
log(
'❌ Error creating the report: ${e.message}',
name: '$_source.followProfile()',
error: e,
);
throw ReportProfileException(
message: e.message,
);
} catch (e) {
log(
'❌ Error creating the report',
name: '$_source.createReport',
error: e,
);
rethrow;
}
}
}