¡Hola! Este documento contiene mis recomendaciones personales para políticas, procedimientos y estándares sobre cómo se debe realizar el desarrollo.
El código del proyecto debe estructurarse con una mezcla de carpeta por función y carpeta por tipo .
Es decir, en el nivel de raíz, tiene carpetas que definen características. Por ejemplo:
|-- common/
|-- authentication/
|-- settings/
La función Common/Core contiene clases, funciones o widgets que, por defecto, deberían estar disponibles en cualquier característica.
Entonces, la idea es tener características de su aplicación divididas por carpetas de nivel superior.
Piense en cada carpetas de nivel superior como un paquete de dardo/flutador separado, dependencias o límites de acceso de su aplicación.
Entonces, si agrega algo a la carpeta común, debe preguntarse, ¿el 99.% de las características de nuestra aplicación lo necesitarán?
Si lo considera una dependencia, la dependencia común siempre se declara en su pubspec.yaml.
Entonces, antes de agregar una clase, función o un widget a la carpeta común, pregúntese:
Dentro de cada carpeta de características tiene carpetas definidas por tipo.
Aquí están los tipos de carpetas que serían parte de una carpeta de características.
|-- ui/
|-- cubits/
|-- domain_objects/
|-- exceptions/
|-- use_cases
|-- services/
|-- repositories/
|-- data_sources/
|-- dtos/
Si hay más de un archivo relacionado con un solo archivo, puede agruparlos en una carpeta, por ejemplo, el código generado para una clase en un archivo. Por ejemplo:
|-- authentication/
|---- cubits/
|------ login/
|-------- login_cubit.dart
|-------- login_cubit.freezed.dart
|-------- login_cubit.g.dart
Código relacionado con la interfaz del dispositivo del usuario, por ejemplo: constructores de páginas de interfaz de usuario, pantallas de interfaz de usuario, vista de interfaz de usuario, componentes de UI.
Una pantalla es un componente de interfaz de usuario que llena toda la pantalla del dispositivo y es el contenedor de la vista o componente de la interfaz de usuario (botón, casillas de verificación, imágenes y ECT).
Además, una pantalla es un destino específico de navegación de aplicaciones.
Pauta:
class LoginScreen extends StatelessWidget {
//...fields and constructor...
@override
Widget build ( BuildContext context) {
return Column (
children : < Widget > [
_LoginLogo ,
_LoginForm ,
_LoginActions ,
_LoginFooter
],
);
}
}
// extracted by use case.
class _LoginLogo extends StatelessWidget {}
// extracted by update area.
class _LoginForm extends StatefullWidget {}
// extracted by update area.
class _LoginActions extends StatelesssWidget {}
// extracted by update area.
class _LoginFooter extends StatelessWidget {} login_screen.dart
part 'login_screen_components.dart' ;
class LoginScreen extends StatelessWidget {
//...fields and constructor...
@override
Widget build ( BuildContext context) {
return Column (
children : < Widget > [
_LoginLogo ,
_LoginForm ,
_LoginActions ,
_LoginFooter
],
);
}
}
// Is kept here because it's does not break the 400 max line rule.
class _LoginLogo extends StatelessWidget {} login_screen_components.dart
part of 'login_screen.dart' ;
/// [LoginScreen] 's fields.
class _LoginForm extends StatelessWidget {}
// ************************ Footer ************************
/// [LoginScreen] 's footer.
class _LoginFooter extends StatelessWidget {}
// ************************* ACTIONS *********************************
/// [LoginScreen] 's actions.
class _LoginActions extends StatelessWidget {} class LoginScreen extends StatelessWidget {
@override
Widget build ( BuildContext context) {
return BlocBuilder < LoginCubit , LoginCubitState >(
// Here cubit is not specified either.
builder : ( BuildContext context, LoginCubitState state) {},
);
}
} class SomeWidget extends StatelessWidget {
@override
Widget build ( BuildContext context) {
//...
}
}
class _SubTree1 extends StatelessWidget {}
class _SubTree2 extends StatelessWidget {}
class _SubTree3 extends StatelessWidget {}
class _SubTree4 extends StatelessWidget {} class LoginScreen extends StatelessWidget {
@override
Widget build ( BuildContext context) {
return Column (
childreen : < Widget > [
Button1 (onClick : _openRegisterUser),
Button2 (onClick : _openLogin),
Field (onTextChanged : _onUserNameTextChanged),
],
);
}
void _openRegisterUser () {}
void _openLogin () {}
void _onUserNameTextChanged ( String newText) {}
}Una vista es un componente de interfaz de usuario que no llena toda la pantalla del dispositivo y es el contenedor de la vista o componente de la interfaz de usuario (botón, casillas de verificación, imágenes y ECT).
Además, una vista no es un destino de navegación de aplicaciones.
Pauta:
Los codos son clases que contienen lógica de negocios para su interfaz de usuario, Cubit está vinculado a un estado, que representan el estado de la UI.
Recomendaciones de directriz:
Future < String ?> bookAppointment ( BookingData booking);
Future < bool > login ( String username, String password);Una clase estatal de cúbito representa el estado de la interfaz de usuario limitado a él un momento determinado.
Recomendaciones de directriz:
class LoginState {
bool logginSuceeded // avoid this.
}Clases que se utilizan para hacer una sola operación comercial/objetivo/trabajo/tarea en su dominio.
Pautas:
class GetCurrentUserUseCase , class SignInUseCase ;UseCase ;call() , donde el tipo de retorno es el resultado de ejecutar el caso de uso;Repositories , Services o cualquier otro high level coordinators ;DataSource o Cubit o interactuar con DTO o cualquier otro objeto de bajo nivel;Cubit o en otros UseCases .Ejemplos:
class GetCurrentUserUseCase {
final UserRepository _repository;
const GetCurrentUserUseCase ( this ._repository);
Future < User ?> call () async {
await _repository. getCurrentUser ();
}
} class AuthenticateMemberUseCase {
/// Create a [AuthenticateMemberUseCase] .
const AuthenticateMemberUseCase (
/* constructor arguments */
);
/* ... fields ...*/
/// Execute the use case.
Future < TegTask < void >> call ( MemberAuthenticationCredentials credentials) {
return runTaskSafelyAsync < void >(() async {
final bool isEmailValid = credentials.email.isEmail;
if ( ! isEmailValid) {
throw const TegEmailInvalidException ();
}
final bool isConnected = await _hostDeviceInternetService. isConnected ();
if ( ! isConnected) {
throw const TegInternetUnavailableException ();
}
final String ? deviceId = await _hostDeviceInfoRepository. getDeviceId ();
if (deviceId == null ) {
throw const TegDeviceIdUnavailableException ();
}
final PublicPrivateKeyPair keyPair = await _keyGenerator. generate ();
final Member member = await _authService. signIn (
email : credentials.email,
password : credentials.password,
deviceId : deviceId,
publicKey : keyPair.publicKey,
);
await _updateCurrentMemberUseCase (
member : member,
memberPrivateKey : keyPair.privateKey,
deviceId : deviceId,
);
await _saveMemberAuthenticationCredentialsUseCase (credentials);
final TegTask < List < Account >> memberAccountsTask = await _getMemberAccountsUseCase ();
if (memberAccountsTask.failed) {
throw memberAccountsTask.exception;
}
await _updateMemberCurrentAccountUseCase (
member : member,
deviceId : deviceId,
memberPrivateKey : keyPair.privateKey,
account : memberAccountsTask.result.first,
);
});
}
}Clases que proporcionan acceso a datos utilizando solo operaciones CRUD para una característica específica o un alcance de una característica, por ejemplo:
abstract class BookingRepository {
Futute < Booking > getBookingById ( String id);
Future < List < Booking >> getBookings ();
Future < void > deleteBookingById ( String id);
Future < void > saveBooking ( Booking booking);
}Recomendaciones de directriz:
class DefaultBookingRepository implements BookingRepository {
final LocalBookingDataSource local;
final RemoteBookingDataSource remote;
Future < Booking ?> getBookingById ( String id) async {
Booking ? savedBooking = await local. getBookingById (id);
if (savedBooking == null ) {
Booking ? remoteBooking = await remote. getBookingById (id);
if (remoteBooking != null ) {
await local. saveBooking (remoteBooking);
}
return remoteBooking;
}
return savedBooking;
}
//...other operations...
}Interfaz
abstract class BookingRepository {}Implementación
class DefaultBookingRepository implements BookingRepository {}
class AnonymousBookingRepository implements BookingRepository {}
class PremiumBookingRepository implements BookingRepository {}Clases que proporcionan acceso a funcionalidades para una característica específica o un alcance de una característica, por ejemplo:
abstract class AuthenticationService {
Future < void > authenticate ( String username, String password);
//...other functionalities...
}
abstract class AppointmentService {
Future < void > register ( Appointment appointment);
//...other functionalities...
}Recomendaciones de directriz.
Clases que implementan acceso a datos ubicados locales o remotamente. Ejemplo
abstract class LocalBookingDataSource {
Futture < void > saveBooking ( Booking booking);
//...other functionalities...
}
abstract class RemoteBookingDataSource {
Future <booking> saveBooking ();
}Recomendaciones de directriz:
class SQLiteBookingDataSource implements LocalBookingDataSource {
//...............Implementation................
}
class RestApiBookingDataSource implements RemoteBookingDataSource {
//...............Implementation................
}
class MemoryBookingDataSource implements LocalBookingDataSource {
//...............Implementation................
}Objetos de transferencia de datos: utilizados para transferir datos de un sistema a otro. Ejemplo
class ApiRequest {
//...fields...
}
class ApiResponse {
//...fields...
} class SQLiteTableDefinition {
//...fields...
}Pautas:
class ApiRequest {
//...fields...
factory ApiRequest . fromDO ( DOObject doObject) {
//...mapper code...
}
DOObject toDO () {
//...mapper code...
}
}El objeto de dominio es la representación de clases de datos de una parte del negocio o un elemento dentro de él, se utilizan para ejecutar la lógica comercial independientemente de la plataforma, la biblioteca o las herramientas.
Un objeto de dominio puede representar, por ejemplo, una persona, lugar, evento, proceso comercial o concepto y factura, un producto, una transacción o incluso detalles de una persona .
Pauta:
Lo mismo que para la estructura del código de proyecto.
Primero primero el nombre de la función, luego la carpeta assets que contiene archivos.
Ejemplo:
|-- authentication/
|---- assets/
|------ password_hidden_icon.svg
|------ forget_password_icon.svg
|------ background.png