Hace un tiempo atrás, hablamos de arquitectura limpia en Flutter. Desde entonces ha cambiado la tecnología, los paquetes y algunos conceptos se refinaron. Por ello, en este artículo te hablaré de cómo puedes implementarla este año.

Antes de comenzar, si sientes que ya conoces los conceptos básicos de arquitectura limpia, puedes continuar al siguiente tema, allí empezaremos a ver a profundidad Clean Architecture aplicado en Flutter.
Arquitectura de Software
Es la parte del desarrollo de soluciones que permite estructurar nuestros proyectos y definir los lineamientos que tendremos que seguir en durante la implementación de los componentes de nuestra solución. Su principal objetivo es que el desarrollo sea fácil de implementar, operar y mantener.
Cuando logramos diseñar una buena arquitectura de software, a nuestra solución le será fácil adaptarse al cambio de forma oportuna sin alterar su estabilidad.
También, la arquitectura de nuestra solución debería buscar la independencia entre sus diferentes capas. Por ejemplo, si en la evolución de nuestra solución, debemos cambiar la base de datos, una API o cualquier otro componente de nuestro desarrollo; esto no debería afectar la capa de presentación. La arquitectura solo deberá verse afectada cuando cambian de manera drástica las reglas del negocio.
En efecto: Es un arquitectura de software diseñada por Robert C. Martin, que consiste en conjunto de capas bien definidas, las cuales están centradas en el dominio (el negocio y sus reglas). Puedes encontrar su libro acá: Clean Architecture: A Craftsman's Guide to Software Structure and Design, First Edition.

El diagrama anterior es el más común a la hora de hablar de Arquitectura limpia. Este nos indica cómo, el Tío Bob (apodo de Robert), describe que debemos dividir nuestras soluciones en su libro. Este parte desde las capas del negocio como, entidades y casos de uso; para luego llegar a la capa de presentación.
Es importante que sepas que el Tío Bob nos enseña en su libro algunos conceptos sujetos a su interpretación. Por lo tanto, podemos deducir que, no existe una única forma de implementar Clean Architecture. Lo que sí podemos afirmar, es que busca la segregación de responsabilidades y centralizar nuestra solución en el dominio.

Como vemos tenemos carpetas por cada una de las capas, sin embargo su contenido debe respetar los principios de la arquitectura limpia. Por ejemplo, bajo ningún motivo la capa de dominio dependerá de infraestructura, tampoco la capa de presentación dependerá de ella. Logrando así un desacople en la solución.
Podemos considerar la capa de Configuración como una capa más externa que nos ayudará con las configuraciones de nuestra aplicación.
Apliquemos esto a un ejemplo sencillo para que podamos interiorizar su implementación. Para ello emplearé de ejemplo la siguiente API, la cual devuelve un dato de astronomía diferente cada día: https://go-apod.herokuapp.com/apod.
Para el ejemplo utilizaré Riverpod como gestor de estados y la inversión de dependencias (ahora mas adelante veremos su importancia).
Capa de Dominio

Comencemos creando nuestra identidad:
// To parse this JSON data, do
//
// final astronomyDailyData = astronomyDailyDataFromJson(jsonString);
import 'dart:convert';
AstronomyDailyData? astronomyDailyDataFromJson(String str) => AstronomyDailyData.fromJson(json.decode(str));
String astronomyDailyDataToJson(AstronomyDailyData? data) => json.encode(data!.toJson());
class AstronomyDailyData {
AstronomyDailyData({
this.date,
this.explanation,
this.hdurl,
this.mediaType,
this.serviceVersion,
this.title,
this.url, required String copyright,
});
final DateTime? date;
final String? explanation;
final String? hdurl;
final String? mediaType;
final String? serviceVersion;
final String? title;
final String? url;
factory AstronomyDailyData.fromJson(Map<String, dynamic> json) => AstronomyDailyData(
date: DateTime.parse(json["date"]),
explanation: json["explanation"],
hdurl: json["hdurl"],
mediaType: json["media_type"],
serviceVersion: json["service_version"],
title: json["title"],
url: json["url"], copyright: '',
);
Map<String, dynamic> toJson() => {
"date": "${date!.year.toString().padLeft(4, '0')}-${date!.month.toString().padLeft(2, '0')}-${date!.day.toString().padLeft(2, '0')}",
"explanation": explanation,
"hdurl": hdurl,
"media_type": mediaType,
"service_version": serviceVersion,
"title": title,
"url": url,
};
}