Monorepo Turborepo: Arquitectura Cross-Platform para React, React Native y Electron

Diagrama técnico mostrando Web, Desktop y Mobile conectados por una capa de design system (packages/ui, packages/ui-mobile, packages/ui-core)

1. Introducción

El desafío de compartir código entre múltiples plataformas representa uno de los problemas arquitectónicos más complejos en el desarrollo moderno de aplicaciones. Imagina esta situación: tienes una aplicación SaaS completa gestionada en un monorepo Turborepo con un stack tecnológico que incluye:

Backend: Node.js con Prisma ORM y PostgreSQL

App Web: React 18+ con Vite y TypeScript

App Desktop: Electron envolviendo la aplicación web

App Mobile: React Native con Expo SDK 50+

Shared Code: Package centralizado con tipos, hooks y utilidades comunes

En teoría, suena perfecto. Sin embargo, la realidad te golpea cuando te das cuenta de que los componentes de interfaz de usuario no son compatibles entre plataformas. Puedes compartir entre Web y Desktop (ambos usan DOM), pero React Native es un ecosistema completamente diferente.

Este artículo documenta la arquitectura que diseñamos para ORDO: The Creator’s OS, una plataforma que evolucionó desde una simple aplicación de gestión de tareas hasta un sistema operativo integral para creadores. Te proporcionaré un análisis técnico profundo con código production-ready, métricas de rendimiento, y decisiones arquitectónicas fundamentadas que puedes aplicar directamente en tu proyecto.

1.1 Contexto y Motivación

ORDO-Todo comenzó como otra aplicación más en un “mar rojo de competencia” saturado de herramientas de productividad. Tras análisis exhaustivos con múltiples modelos de lenguaje (Gemini, Claude Sonnet, Copilot), identificamos una oportunidad de mercado: transformarlo en “ORDO: The Creator’s OS”, un sistema operativo para creadores que gestiona workspaces, proyectos, tareas y flujos de trabajo de manera unificada.

Esta visión ambiciosa requería presencia omnipresente: los creadores trabajan en laptops (Desktop), necesitan acceso rápido desde navegadores (Web), y requieren movilidad (Mobile). La arquitectura técnica debía soportar estas tres plataformas sin triplicar el esfuerzo de desarrollo.

1.2 El Problema Fundamental

La incompatibilidad de componentes UI entre plataformas no es un problema menor; es un obstáculo arquitectónico que afecta:

Productividad del equipo: Mantener tres implementaciones separadas del mismo componente es costoso y propenso a errores

Consistencia de UX: Diferencias sutiles en comportamiento entre plataformas generan confusión en usuarios

Mantenibilidad: Cada corrección de bug o mejora debe replicarse tres veces

Testing: La superficie de pruebas se multiplica por el número de plataformas

Technical debt: La duplicación de código crea deuda técnica que crece exponencialmente

Este artículo presenta una solución arquitectónica que comparte el 80% del código (lógica de negocio, validación, state management) mientras mantiene implementaciones UI específicas por plataforma para aprovechar las optimizaciones nativas.

2. Análisis del Problema: Incompatibilidad Técnica

2.1 Diferencias Fundamentales entre Plataformas

Comprendamos primero por qué los componentes no son compatibles. La raíz del problema radica en que Web y React Native usan primitivas de renderizado completamente diferentes:

// ============================================================================
// WEB/DESKTOP (DOM-based rendering)
// ============================================================================
import { Dialog } from '@radix-ui/react-dialog';
import { buttonVariants } from '@/components/ui/button';

export function TaskDialog() {
  return (
    <Dialog>
      <DialogTrigger className={buttonVariants({ variant: 'primary' })}>
        Abrir Diálogo
      </DialogTrigger>
      <DialogContent className="bg-white rounded-lg p-6 shadow-xl">
        <DialogHeader>
          <DialogTitle className="text-xl font-semibold">Nueva Tarea</DialogTitle>
        </DialogHeader>
        {/* Form content */}
      </DialogContent>
    </Dialog>
  );
}

// ============================================================================
// REACT NATIVE (Native rendering)
// ============================================================================
import { Modal, View, Text, TouchableOpacity } from 'react-native';

export function TaskModal() {
  const [visible, setVisible] = useState(false);

  return (
    <>
      <TouchableOpacity onPress={() => setVisible(true)}>
        <Text className="bg-primary text-white px-4 py-2 rounded-lg">
          Abrir Modal
        </Text>
      </TouchableOpacity>
      <Modal visible={visible} animationType="slide">
        <View className="bg-white rounded-lg p-6">
          <Text className="text-xl font-semibold">Nueva Tarea</Text>
          {/* Form content */}
        </View>
      </Modal>
    </>
  );
}

Las diferencias van más allá de la sintaxis. Analicemos una comparación sistemática:

| Primitiva | Web/Desktop (DOM) | React Native | Impacto |

|—————|———————-|——————|————-|

| Botones | `

| Inputs | ``, `