Saltar al contenido
CrossFly es 100% open sourceSíguenos en GitHub
CrossFlyCrossFly
Empezar

Esta página fue traducida automáticamente del portugués y puede contener errores. La versión en portugués es la canónica.

Arquitectura de app

CrossFly no impone arquitectura y no tiene DI ni binding reactivo (ver Estado y flujo de datos). Eso es libertad — y responsabilidad. A continuación, convenciones sugeridas (compatibles, no obligatorias) que escalan. Es el recorte de tarea del Capítulo 32.

Estructura de paquetes sugerida

app/
 ├─ ui/screens/     → uma tela = uma subclasse de Window
 ├─ viewmodel/      → estado + lógica de apresentação por tela
 ├─ data/repository → fachada sobre dev.crossfly.sqlite / rede
 └─ service/        → serviços de app (singletons) + service locator

Una pantalla = una Window

public class ClientesScreen extends Window {
   private final ClientesVM vm = new ClientesVM(Services.get(ClienteRepository.class));
   @Override public void onShow() throws Exception {
      super.onShow();               // arma la UI aquí (bounds válidos)
      // … conecta UI ↔ vm …
      vm.observe(this::render);     // re-renderiza cuando el estado cambia
      vm.carregar();
   }
   @Override public void onRemove() { vm.dispose(); super.onRemove(); }  // libera recursos
}

ViewModel ligero (sin setState)

Como no hay binding reactivo, el ViewModel guarda estado y expone un observe(Runnable); la View re-renderiza manualmente:

public class ClientesVM {
   private final ClienteRepository repo;
   private Runnable onChange;
   public List<Cliente> clientes = List.of();
   public ClientesVM(ClienteRepository repo) { this.repo = repo; }
   public void observe(Runnable r) { this.onChange = r; }
   public void carregar() {
      new Thread(() -> {
         List<Cliente> dados = repo.listar();
         Component.onUI(() -> { clientes = dados; if (onChange != null) onChange.run(); });
      }).start();
   }
   public void dispose() { onChange = null; }
}

Repository sobre SQLite

Aísla el acceso a datos en una fachada — la UI nunca habla SQL directo:

public class ClienteRepository {
   public List<Cliente> listar() {
      try (SqliteDb db = SqliteDb.open("app.db");
           SqliteCursor c = db.query("SELECT id, nome FROM cliente")) {
         List<Cliente> out = new ArrayList<>();
         while (c.next()) out.add(new Cliente(c.getLong(0), c.getString(1)));
         return out;
      }
   }
}

En lugar de DI: un service locator simple

La antigua Registry fue eliminada. Un Map<Class,Object> propio resuelve (o inyección por constructor):

public final class Services {
   private static final Map<Class<?>, Object> reg = new HashMap<>();
   public static <T> void put(Class<T> k, T v) { reg.put(k, v); }
   public static <T> T get(Class<T> k) { return k.cast(reg.get(k)); }
}
// en el bootstrap: Services.put(ClienteRepository.class, new ClienteRepository());

Buenas prácticas

  • UI en onShow(), recursos liberados en onRemove().
  • Estado en el ViewModel; las mutaciones vuelven a la paint thread vía onUI; la View traduce estado→componentes.
  • El Repository esconde SQLite/red; la pantalla no conoce SQL.

Errores comunes

  • Buscar DI/observables de Flutter — no existen; usa service locator + observe(...) manual.
  • Poner SQL/HTTP directo en la Window (acopla y traba la UI).
  • Guardar estado de app en campos de la Window (desaparece al navegar).

Ver también

¿Te resultó útil esta página?