Ir al contenido

Arquitectura ​

Arquitectura técnica y decisiones de diseño de Mindwtr.


Descripción general ​

Mindwtr es una aplicación GTD multiplataforma con:

  • Aplicación de escritorio — Tauri v2 (Rust + React)
  • Aplicación móvil — React Native + Expo
  • Servidor MCP — puente local del Model Context Protocol para herramientas de IA
  • Sincronización en la nube — servidor de sincronización Node.js (Bun)
  • Núcleo compartido — paquete de lógica de negocio en TypeScript

La aplicación de escritorio, la aplicación móvil y el servidor MCP comparten un único paquete core que contiene el estado, las reglas de tareas y la fusión de sincronización. Cada dispositivo tiene su propia base de datos SQLite, y la sincronización escribe una instantánea de ella en el destino que elijas: una carpeta de File Sync, un servidor WebDAV, Dropbox, un Mindwtr Cloud autoalojado o iCloud. Nada sale del dispositivo hasta que se ejecuta un ciclo de sincronización, y en los tres primeros destinos una contraseña opcional lo sella antes.

Decisiones de diseño y sus implicaciones ​

  • La sincronización en la nube se basa en archivos y está optimizada para el autoalojamiento en una sola máquina.
  • Las claves foráneas de SQLite se aplican para garantizar la integridad de los registros activos, mientras que la reparación de eliminaciones lógicas y registros de eliminación se sigue realizando en la lógica compartida de la aplicación.
  • Las eliminaciones definitivas son poco frecuentes, pero existen. sections.projectId usa ON DELETE CASCADE, mientras que las referencias de tareas, proyectos y áreas usan principalmente ON DELETE SET NULL.

Diagrama del sistema ​

Cómo encaja Mindwtr: las dos aplicaciones y el servidor MCP llaman al paquete core compartido, que guarda en una base de datos SQLite local, y desde ahí el ciclo de sincronización escribe en el destino que elijas.

Abrir el diagrama interactivo


Estructura del monorepo ​

El proyecto usa un monorepo con espacios de trabajo de Bun:

Mindwtr/
├── apps/
│   ├── cloud/           # Sync server (Bun)
│   ├── desktop/         # Tauri app
│   ├── mcp-server/      # Local MCP server
│   └── mobile/          # Expo app
├── packages/
│   └── core/            # Shared business logic
└── package.json         # Workspace root

Ventajas ​

  • Código compartido entre plataformas
  • Una sola versión de las dependencias
  • Pruebas e integración continua unificadas
  • Refactorización más sencilla

Paquete principal (@mindwtr/core) ​

El paquete principal contiene toda la lógica de negocio compartida:

Módulos ​

MóduloPropósito
store.tsAlmacén de estado Zustand con todas las acciones
types.tsInterfaces TypeScript (Task, Project, etc.)
i18n/i18n-loader.tsCarga diferida de traducciones
i18n/i18n-locales.tsUn descriptor por idioma: cargadores y mínimo de cobertura
i18n/locales/*.tsConfiguración regional base en inglés y sobrescrituras por idioma
i18n/starter-seed-strings.tsCadenas generadas de la semilla inicial (no editar a mano)
contexts.tsContextos y etiquetas predefinidos
quick-add.tsAnalizador de tareas en lenguaje natural
recurrence.tsLógica de tareas recurrentes (RFC 5545 parcial)
sync.ts + sync-*.tsNúcleo de fusión de sincronización y funciones auxiliares compartidas; consulta la lista de módulos a continuación
date.tsFunciones para el análisis seguro de fechas
ai/Integración con IA (Gemini/OpenAI/Anthropic)
storage.tsInterfaz del adaptador de almacenamiento local
sqlite-adapter.tsImplementación compartida de almacenamiento SQLite
webdav.tsCliente de sincronización WebDAV

Los submódulos de sincronización actuales dividen el protocolo por responsabilidad: sync-run.ts es la máquina de estados compartida del ciclo de sincronización (secuencia de fases, comprobaciones para omitir datos sin cambios, fases de adjuntos, gestión de errores y de volver a poner en cola) que se encuentra detrás de los puertos de sync-run-ports.ts; las aplicaciones de escritorio y móvil proporcionan adaptadores de transporte, almacenamiento y notificaciones (ADR 0014). sync-orchestrator.ts serializa los ciclos y pone en cola los posteriores, sync-normalization.ts repara la forma de la carga útil, sync-signatures.ts calcula firmas de contenido comparables, sync-merge-settings.ts fusiona grupos de ajustes, sync-tombstones.ts gestiona la limpieza según la retención, sync-revision.ts asigna revisiones y sync-client-helpers.ts / sync-service-utils.ts contienen funciones auxiliares de los servicios de plataforma.

Principios de diseño ​

  1. Independiente de la plataforma — Sin código específico de una plataforma
  2. Patrón de adaptador de almacenamiento — Inyección del almacenamiento en tiempo de ejecución
  3. Funciones puras — Las utilidades no tienen estado
  4. Seguridad de tipos — Cobertura completa de TypeScript

Capas del estado ​

  • El almacén principal conserva los datos canónicos (all tasks/projects).
  • Los almacenes de la interfaz contienen filtros específicos de cada vista y el estado de la interfaz.
  • Las listas visibles se derivan de los datos principales y los filtros de la interfaz para evitar mezclar las responsabilidades de persistencia con la presentación.

Arquitectura de escritorio (Tauri) ​

¿Por qué Tauri? ​

CaracterísticaTauriElectron
Tamaño del binario~5 MB~150 MB
Uso de memoria~50 MB~300 MB
BackendRustNode.js
Vista webSistemaChromium incluido

Estructura ​

apps/desktop/
├── src/                         # React frontend
│   ├── App.tsx                  # Root component and app shell wiring
│   ├── main.tsx                 # Vite/Tauri webview entry
│   ├── components/
│   │   ├── Task/                # Task form, field, and editor components
│   │   ├── ui/                  # Shared primitive UI components
│   │   └── views/               # Feature views
│   │       ├── agenda/
│   │       ├── calendar/
│   │       ├── inbox/
│   │       ├── list/
│   │       ├── projects/
│   │       ├── review/
│   │       └── settings/
│   ├── config/                  # Desktop app constants/config
│   ├── contexts/                # React contexts
│   ├── hooks/                   # Shared React hooks
│   ├── lib/                     # Desktop services and Tauri bridges
│   ├── store/                   # UI-specific state
│   ├── test/                    # Desktop test utilities
│   └── utils/                   # Small shared utilities
│
├── src-tauri/                  # Rust backend
│   ├── src/main.rs             # Entry point
│   ├── src/platform.rs         # Native commands and path validation
│   ├── capabilities/           # Tauri command/plugin permissions
│   ├── Cargo.toml              # Rust dependencies
│   └── tauri.conf.json         # Tauri config
│
└── package.json

Flujo de datos ​

User Action → React Component → Zustand Store (@mindwtr/core) → Storage Adapter → SQLite + data.json

Comandos de Tauri ​

El backend en Rust expone comandos para:

  • Abrir archivos permitidos y realizar operaciones con adjuntos y almacenamiento
  • Diálogos nativos
  • Notificaciones del sistema

Arquitectura móvil (Expo) ​

¿Por qué Expo? ​

  • El flujo de trabajo gestionado simplifica el desarrollo
  • Capacidad para actualizaciones OTA
  • Expo Router para navegación basada en archivos
  • Proceso de compilación sencillo (EAS)

Estructura ​

apps/mobile/
├── app/                   # Expo Router pages
│   ├── (drawer)/         # Drawer navigation
│   │   ├── (tabs)/       # Tab navigation
│   │   │   ├── calendar-tab.tsx
│   │   │   ├── capture-quick.tsx
│   │   │   ├── inbox.tsx
│   │   │   ├── focus.tsx
│   │   │   ├── capture.tsx
│   │   │   ├── contexts-tab.tsx
│   │   │   ├── projects.tsx
│   │   │   ├── review-tab.tsx
│   │   │   └── menu.tsx
│   │   ├── calendar.tsx
│   │   ├── contexts.tsx
│   │   ├── saved-search/[id].tsx
│   │   ├── board.tsx
│   │   ├── waiting.tsx
│   │   ├── someday.tsx
│   │   ├── done.tsx
│   │   ├── trash.tsx
│   │   ├── archived.tsx
│   │   ├── reference.tsx
│   │   ├── projects-screen.tsx
│   │   └── settings.tsx
│   └── _layout.tsx       # Root layout
│
├── components/           # Shared components
├── contexts/             # Theme, Language
├── lib/                  # Storage, sync utilities
└── package.json
Drawer/Stack Layout
├── Tab Navigator
│   ├── Enfoque
│   ├── Bandeja de entrada
│   ├── Captura
│   ├── Acceso rápido (Proyectos, Calendario, Contextos o Revisión)
│   └── Menú (enlaces a otras vistas)
├── Other Screens (Stack)
│   ├── Board
│   ├── Calendar
│   ├── Review
│   ├── Contexts
│   ├── Waiting For
│   ├── Someday/Maybe
│   ├── Archived
│   └── Settings

Gestión del estado ​

Almacén Zustand ​

El almacén central (@mindwtr/core/src/store.ts) gestiona todo el estado de la aplicación:

typescript
interface TaskStore {
    tasks: Task[];
    projects: Project[];
    areas: Area[];
    settings: AppData['settings'];

    // Actions
    fetchData: () => Promise<void>;
    addTask: (title: string, props?: Partial<Task>) => Promise<StoreActionResult>;
    updateTask: (id: string, updates: Partial<Task>) => Promise<StoreActionResult>;
    deleteTask: (id: string) => Promise<StoreActionResult>;
    // ... projects, areas, and settings actions
}

Patrón de adaptador de almacenamiento ​

El almacén usa adaptadores de almacenamiento inyectados:

typescript
// Escritorio: SQLite mediante Tauri
setStorageAdapter(tauriStorage);

// Mobile: SQLite (with JSON backup fallback)
setStorageAdapter(mobileStorage);

Persistencia ​

  • Agrupación de escrituras — Los cambios se ponen en cola de inmediato y las escrituras superpuestas se agrupan en el siguiente volcado
  • Volcado al salir — Los guardados pendientes se vuelcan cuando la aplicación pasa a segundo plano
  • Eliminaciones lógicas — Los elementos se marcan con deletedAt para la sincronización

Modelo de datos ​

La superficie de tipos canónica se encuentra en la API principal y en packages/core/src/types.ts.

  • Consulta la API principal para ver la documentación actual de cada campo de Task, Project, Section, Area, Person, Attachment y AppData.
  • Los campos sensibles a la sincronización, como rev, revBy, purgedAt, orderNum, mimeType, size, cloudKey y localStatus, evolucionan con más frecuencia que esta descripción general de la arquitectura.
  • Mantener el volcado detallado de tipos en una sola página evita que la documentación de arquitectura se desvíe del código.

Estrategia de sincronización ​

LWW consciente de revisiones con registros de eliminación ​

La sincronización de datos se basa en «última escritura gana» con reconocimiento de revisiones y criterios deterministas para deshacer empates.

Lógica de fusión ​

  1. Resolución:
    • Si ambos lados tienen revisiones, gana el rev más alto antes de recurrir a las marcas de tiempo.
    • rev es un contador de ediciones por entidad, no un reloj vectorial, por lo que un lado con más ediciones sin conexión puede imponerse a una única edición más reciente de otro dispositivo.
    • Si las revisiones empatan, se compara updatedAt.
    • Si las marcas de tiempo también empatan, se comparan firmas deterministas del contenido normalizado para que todos los dispositivos elijan al mismo ganador.
    • Las entidades heredadas sin metadatos de revisión tratan los valores de updatedAt dentro del umbral de 5 minutos de desfase del reloj como un empate determinista; fuera de esa ventana, gana el updatedAt más reciente.
  2. Registros de eliminación:
    • Los elementos eliminados conservan su registro con deletedAt establecido.
    • Evita que reaparezcan durante la sincronización.
    • Permite una fusión correcta entre dispositivos.
    • Los conflictos entre eliminación y registro activo usan el momento de la operación (max(updatedAt, deletedAt) para los registros de eliminación).
    • Si las operaciones de eliminación y registro activo tienen lugar dentro de la ventana de ambigüedad de 30 segundos, Mindwtr conserva el elemento activo en lugar de eliminarlo inmediatamente.
  3. Conflictos:
    • Los conflictos de metadatos se resuelven automáticamente.
    • Los ajustes se fusionan por grupos de sincronización (appearance, language, gtd, externalCalendars, ai, savedFilters) en lugar de usar una única marca de tiempo para un objeto enorme.
    • Los conflictos entre filtros guardados activos usan estrictamente el updatedAt de cada filtro, con una alternativa determinista solo cuando las marcas de tiempo son iguales o inutilizables.
    • Las advertencias de gran desfase del reloj se activan cuando la diferencia de fusión supera el umbral actual de 5 minutos.

Ciclo de sincronización ​

1. Read Local Data
2. Read Remote Data (Cloud/WebDAV/File)
3. Merge (Memory) -> Generate Stats (conflicts, updates)
4. Write Local with pending-remote-write marker
5. Write Remote
6. Clear pending-remote-write marker locally

Si la escritura remota falla después de la persistencia local, Mindwtr guarda metadatos de reintento y aumenta progresivamente la espera desde 5 segundos hasta 5 minutos antes de volver a intentarlo.

Transporte de instantáneas ​

Actualmente, la sincronización de Mindwtr transporta instantáneas completas de forma intencionada. No se trata de un sustituto provisional de un sistema de deltas ausente.

  • Los ADR 0003 y ADR 0007 definen las reglas de fusión conscientes de revisiones que se aplican a esas instantáneas.
  • El ADR 0008 registra la decisión de transporte actual: mantener la fusión de instantáneas y no añadir todavía un registro de deltas.
  • Para las cargas de trabajo actuales de GTD personal, la sincronización mediante instantáneas simplifica la implementación, conserva la atomicidad de todo el archivo y evita estados adicionales de reproducción y compactación.
  • Si esto cambia en el futuro, el diseño de deltas debe ampliar el modelo existente de rev y revBy en lugar de sustituirlo por un nuevo sistema de secuencias.

La decisión sobre el registro de deltas solo debe revisarse si los archivos de instantáneas superan habitualmente los 5 MB, los ciclos completos de sincronización tardan más de 5 segundos en redes habituales o el producto necesita transmisión multidispositivo en tiempo real.

La cobertura de pruebas y las condiciones de publicación se registran por separado en Estrategia de pruebas para que esta página pueda centrarse en la arquitectura en tiempo de ejecución.


Internacionalización ​

Estructura ​

Las traducciones se distribuyen dentro de la carpeta packages/core/src/i18n/:

typescript
// packages/core/src/i18n/i18n-loader.ts
// packages/core/src/i18n/i18n-locales.ts
// packages/core/src/i18n/locales/*.ts

Uso ​

Cada aplicación tiene un contexto de idioma que proporciona una función t().

Mindwtr es gratuito, de código abierto y local-first.
Getting Things Done y GTD son marcas registradas de David Allen Company. Mindwtr no está afiliado, respaldado ni patrocinado por David Allen Company.