Principios Comunes de Arquitectura


 ¡Claro! Vamos a desglosar los conceptos de arquitectura mencionados para que los entiendas de forma clara y sencilla. Cada sección tiene ejemplos y analogías que te ayudarán a comprender cómo se aplican específicamente en Flutter.


1. Principios Comunes de Arquitectura


Separación de responsabilidades (Separation of concerns)


¿Qué significa?

Dividir tu aplicación en partes que sean independientes y especializadas. Cada “unidad” de código debe ser responsable de una sola cosa. Esto facilita la modularidad y la mantenibilidad.

Ejemplo real: En una cocina, los cocineros se encargan de preparar los platos, mientras los meseros se encargan de llevarlos a los clientes. Cada uno tiene su rol bien definido, lo que evita confusiones.

En Flutter: Separa la lógica del negocio (cálculos o manejo de datos) de la lógica de UI (cómo se muestran los datos al usuario).


Por ejemplo:

No mezcles código que calcula impuestos con código que crea botones.


Arquitectura en capas (Layered architecture)


¿Qué significa?

Dividir tu aplicación en capas, cada una con un propósito claro. En Flutter, normalmente hay tres capas principales:

1. UI Layer (Capa de presentación):

Qué hace: Muestra los datos al usuario y captura las interacciones del usuario.

Ejemplo: Los widgets de Flutter (como Text, Button) forman esta capa.

Regla: No debería tener lógica de negocio ni conocer la fuente de los datos directamente.

2. Logic Layer (Capa de lógica):

Qué hace: Procesa las reglas de negocio, maneja decisiones y transforma datos.

Ejemplo: Manejar qué productos mostrar según la búsqueda del usuario o calcular precios.

Nota: Es opcional para aplicaciones simples que solo muestran y editan datos (CRUD).

3. Data Layer (Capa de datos):

Qué hace: Gestiona las fuentes de datos, como bases de datos locales o APIs remotas.

Ejemplo: Realizar una llamada HTTP a un servidor o leer un archivo local.


Ejemplo visual de comunicación entre capas:


El mesero (UI Layer) toma el pedido y se lo entrega al chef (Logic Layer), quien se encarga de decidir cómo preparar el plato. El chef usa ingredientes del almacén (Data Layer) y finalmente devuelve el plato al mesero para que lo lleve al cliente.


En Flutter:


[UI Layer] => Solicita datos del Logic Layer

[Logic Layer] => Transforma y solicita datos del Data Layer

[Data Layer] => Proporciona los datos a las capas superiores.


Fuente única de verdad (Single source of truth - SSOT)


¿Qué significa?

Cada tipo de dato debe tener un único lugar donde se almacena y gestiona. Todas las demás partes de tu app deben obtener ese dato desde este lugar.

Ejemplo real: Piensa en una lista de invitados a una boda. Si hay varias listas (una en papel, otra en el móvil y otra en la cabeza del organizador), será muy fácil que haya confusiones. Tener una lista única centralizada elimina errores.

En Flutter:

Usa un repositorio para manejar tus datos. Por ejemplo, si trabajas con usuarios, crea un UserRepository que sea el encargado de acceder, actualizar y exponer esa información.


Ejemplo básico de repositorio:


class UserRepository {

  final _users = <String>['Alice', 'Bob'];


  List<String> getUsers() => _users;


  void addUser(String user) {

    _users.add(user);

  }

}


Flujo de datos unidireccional (Unidirectional Data Flow - UDF)


¿Qué significa?

Los datos deben fluir en una sola dirección, desde la fuente de datos (Data Layer) hasta la UI Layer, y las interacciones del usuario deben regresar por el camino contrario.


Ejemplo:

1. El usuario presiona un botón en la UI Layer.

2. La lógica (Logic Layer) decide qué hacer con ese evento (por ejemplo, agregar un ítem al carrito).

3. La capa de datos (Data Layer) guarda ese cambio en la fuente de verdad.

4. La nueva información regresa por el Logic Layer hacia la UI Layer para ser actualizada.


En Flutter, esta estructura simplifica mucho la app:


1. Evento en la UI Layer → Logic Layer → Data Layer

2. Cambio de datos → Logic Layer → Actualización en la UI Layer


La UI es una función del estado


Flutter usa un enfoque declarativo: la interfaz gráfica (UI) depende del estado actual. Si el estado cambia, la UI se reconstruye automáticamente.

Ejemplo: Piensa en un mapa en tu móvil. Si cambias la ubicación actual, el mapa se actualiza automáticamente para reflejar el nuevo lugar.

En Flutter:

Usa StateNotifier (con Riverpod o similar) para manejar el estado, y deja que los widgets reaccionen automáticamente.


Ejemplo básico:


final counterProvider = StateProvider<int>((ref) => 0);


class CounterApp extends ConsumerWidget {

  @override

  Widget build(BuildContext context, WidgetRef ref) {

    final counter = ref.watch(counterProvider);


    return Scaffold(

      appBar: AppBar(title: Text('Contador')),

      body: Center(

        child: Text('Valor: $counter'),

      ),

      floatingActionButton: FloatingActionButton(

        onPressed: () {

          ref.read(counterProvider.notifier).state++;

        },

        child: Icon(Icons.add),

      ),

    );

  }

}


Extensibilidad


¿Qué significa?

Tu arquitectura debe ser flexible para agregar nuevas funcionalidades sin romper las existentes. Esto se logra con interfaces bien definidas entre las capas.

Ejemplo real: Un tomacorriente estándar acepta cualquier aparato eléctrico compatible. No importa si enchufas un ventilador o un cargador de móvil; la conexión siempre funciona.

En Flutter:

Diseña tus clases para depender de interfaces, no de implementaciones concretas. Esto facilita intercambiar detalles internos si es necesario.


Testeabilidad


Un buen diseño arquitectónico facilita las pruebas automáticas:

Ejemplo: Puedes probar la lógica del negocio (como los cálculos de precios) sin necesitar widgets o acceso a una API.


Resumen práctico


1. Divide tu app en capas:

UI Layer: Widgets que muestran datos.

Logic Layer: Clases que procesan datos y toman decisiones.

Data Layer: Clases que acceden a bases de datos o APIs.

2. Sigue el flujo de datos unidireccional:

Usuario → Logic Layer → Data Layer → Logic Layer → UI Layer

3. Usa una fuente única de verdad para tus datos: repositorios.

4. Diseña tus componentes de forma extensible y testeable.


Si necesitas ejemplos más específicos o tienes dudas en algún punto, ¡aquí estoy! 😊

Comentarios