El segundo eje, la unidad: filas de una tabla o documentos
El segundo eje pregunta cuál es el objeto que viaja y se compara, la fila de una base relacional o el documento con identidad propia, y esa elección condiciona a la vez tu modelo de datos y tu control de acceso.
Fijada la cuestión de quién tiene la última palabra, el segundo eje pregunta algo aparentemente más humilde y con consecuencias igual de profundas: cuál es el objeto que viaja por el cable, se identifica, se versiona y se compara. Hay dos respuestas dominantes y no se distinguen por ser mejores o peores sino por acoplar cosas distintas. Una dice que la unidad es la fila, una tupla con clave primaria dentro de una tabla con esquema declarado, y es la que adoptan los sistemas que hacen llegar tablas de Postgres al cliente o que mantienen SQLite completo en local. La otra dice que la unidad es el documento, un agregado con identidad propia y estructura interna que se carga y se fusiona como un todo. La tentación es leer esto como la vieja disputa entre lo relacional y lo documental trasladada al navegador, y es un error de nivel: aquí la unidad no decide solo cómo consultas, decide también qué puedes prohibir, a quién, y con qué precisión, porque la unidad de sincronización acaba siendo también la unidad de permiso.
- Describir qué viaja exactamente en un motor de filas y en uno de documentos, y qué reconstruye el cliente con ello.
- Entender por qué la réplica parcial convierte la referencia colgante en el estado normal y no en una anomalía.
- Reconocer cómo cada unidad empuja tu modelo hacia la normalización o hacia el agregado, y qué falla en cada extremo.
- Ver que la unidad determina la granularidad máxima de tu control de acceso y por qué eso la ata al eje de la autoridad.
Filas: la unidad es la tupla con esquema
Cuando la unidad es la fila, lo que circula son cambios a nivel de tupla —alta, modificación, baja— identificados por clave primaria dentro de una tabla cuyo esquema ambas partes conocen. El cliente no recibe respuestas preparadas sino material con el que reconstruir una base de datos, y por eso este extremo casi siempre acaba en SQLite del lado del cliente: PowerSync lo dice explícitamente, mantiene SQLite completo en local y admite Postgres, MySQL o MongoDB como origen. La consecuencia práctica es contundente y vale la pena enunciarla sin rodeos: el cliente puede ejecutar consultas que nadie previó en tiempo de diseño, con junturas, agregaciones e índices propios, porque tiene un motor de consultas de verdad y no una colección de respuestas cacheadas.
Esa capacidad tiene una contrapartida que aparece el primer día y no se va nunca. Como el cliente tiene solo una parte del universo, una clave ajena puede apuntar a una fila que no está, y no por corrupción sino por diseño: la fila referenciada quedó fuera del alcance sincronizado. En una base completa esa situación es una violación de integridad referencial que el motor impide; en una réplica parcial es el estado normal de las cosas. Todo el código de cliente que resuelva relaciones tiene que estar escrito asumiendo la ausencia, lo que en la práctica significa que toda juntura es externa, toda búsqueda por identificador puede no devolver nada y ninguna pantalla puede dar por hecho que el objeto relacionado existe localmente.
-- En una replica parcial esto es una fuente inagotable de filas fantasma
SELECT t.id, u.nombre FROM tareas t JOIN usuarios u ON u.id = t.asignado;
-- La forma correcta asume la ausencia y la hace visible en la interfaz
SELECT t.id, COALESCE(u.nombre, 'sin cargar') AS nombre
FROM tareas t LEFT JOIN usuarios u ON u.id = t.asignado;
Hay una variante interesante dentro de este mismo extremo que conviene distinguir, porque cambia qué significa la palabra alcance. Un motor puede hacer llegar tablas o fragmentos de tablas, y entonces el cliente decide después qué consultar; o puede hacer llegar resultados de consultas y mantenerlos vivos, de modo que lo que el cliente tiene son conjuntos de filas que satisfacen predicados declarados. La unidad sigue siendo la fila en ambos casos, pero en el segundo la pertenencia de una fila a la copia local depende de un predicado que puede dejar de cumplirse, y entonces la fila tiene que salir. Ese detalle es el que hace la cuarta lección menos obvia de lo que parece.
Documentos: la unidad es el agregado con identidad
Cuando la unidad es el documento, lo que circula es un agregado con identificador propio y estructura interna arbitrariamente anidada, que se carga entero y se fusiona con reglas definidas para el documento completo. La diferencia decisiva respecto del extremo anterior no está en la forma de los datos sino en una coincidencia: en un sistema de documentos, la unidad de sincronización, la unidad de permiso y la unidad de conflicto son el mismo objeto. Se sincroniza un documento, se da acceso a un documento y se fusiona un documento. Esa triple coincidencia es la propiedad que define este extremo, y de ella se derivan casi todas sus virtudes y todos sus problemas.
Las virtudes son considerables. Un documento puede llevar dentro estructura rica sin pagar junturas, su semántica de fusión se define una vez y sirve para todo su contenido, y cargar lo que hace falta para una pantalla es una decisión única en lugar de un plan de consulta. Y hay una virtud que solo se aprecia al mirar el otro eje: como el permiso se ata a una identidad única, se puede expresar con una clave criptográfica en lugar de con una regla que alguien tenga que evaluar. Ese es el motivo estructural de que los sistemas que apuestan por el extremo descentralizado y cifrado, como Jazz, sean casi siempre de documentos y no de filas.
Los problemas son igual de nítidos. El primero es el tamaño: un documento que crece sin techo —el canal con todos sus mensajes, el proyecto con todas sus tareas desde el principio de los tiempos— se convierte a la vez en un problema de descarga, de memoria y de fusión, porque todo eso se hace sobre la pieza entera. Los sistemas de documentos obligan por tanto a una política de ciclo de vida que los de filas no piden: documentos rodantes por periodo, archivado explícito, división al superar un umbral. El segundo es que la frontera del documento se decide pronto y se cambia tarde, porque mover esa frontera implica migrar identidades, historiales de fusión y listas de acceso a la vez.
Antes de dibujar fronteras por criterios de tamaño o de comodidad de carga, dibújalas por criterios de compartición. La pregunta que mejor predice si una frontera va a envejecer bien no es cuánto pesa el documento ni cuántas pantallas lo usan, sino si existe algún escenario realista en el que quieras dar acceso a una parte y no a la otra. Si existe, esa costura será tarde o temprano una frontera de documento, y ponerla desde el principio cuesta una tarde mientras que ponerla después cuesta una migración con datos de usuarios en producción. La regla se enuncia en una línea y ahorra meses: las fronteras de documento son fronteras de permiso, así que dibújalas donde el permiso vaya a cambiar.
Cómo la unidad condiciona tu modelo de datos
flowchart TD U[eleccion de unidad] --> F[fila con esquema] U --> D[documento con identidad] F --> FN[empuja a normalizar] F --> FR[referencias colgantes como estado normal] F --> FC[consultas no previstas posibles en local] D --> DA[empuja al agregado desnormalizado] D --> DT[techo de tamano y ciclo de vida obligatorio] D --> DP[permiso expresable con una clave] style F fill:#89b4fa,color:#11111b style D fill:#cba6f7,color:#11111b
Cada unidad ejerce una presión distinta sobre el modelo y esa presión se nota en el código a las pocas semanas. Las filas empujan a normalizar, porque la normalización abarata la sincronización: un cambio en el nombre de una persona viaja una vez y no en las doscientas copias donde ese nombre estaba embebido. Los documentos empujan a desnormalizar, porque el agregado es lo que se carga y lo que se fusiona, y romper un caso de uso en cinco documentos convierte una pantalla en cinco cargas coordinadas cuyos estados de llegada hay que combinar a mano.
Ambas presiones tienen su modo de fallo característico y conviene reconocerlos pronto. El modelo de filas llevado al extremo produce explosión de piezas diminutas: miles de filas baratas por separado que en conjunto pesan más en contabilidad de sincronización, índices y puntos de reanudación que en contenido real, y una pantalla cualquiera acaba dependiendo de que siete tablas distintas hayan llegado. El modelo de documentos llevado al extremo produce la patología inversa, el documento monolítico que lo contiene todo porque nunca hubo un momento cómodo para dividirlo, y cuya carga inicial arruina el arranque en frío que todo este track intenta proteger.
Hay además una diferencia de calendario que rara vez se menciona y que pesa mucho en la práctica. Con filas puedes aplazar decisiones de frontera, porque toda combinación futura de datos es una consulta que aún no has escrito y podrás escribir cuando la necesites. Con documentos tienes que anticiparlas, porque la frontera está grabada en la identidad de los objetos. Esa diferencia hace que el modelo de filas resulte más cómodo cuando el producto todavía se está descubriendo, y que el de documentos resulte más limpio cuando el dominio ya está estabilizado y sus unidades de compartición son evidentes.
Cómo la unidad condiciona tu control de acceso
Aquí el eje enseña lo que de verdad estaba escondiendo. Con filas, el permiso se expresa como un predicado sobre la fila: puedes ver esta fila si su columna de propietario coincide contigo, o si perteneces al equipo al que apunta, o si su estado no es borrador. Es una expresión potentísima, dinámica y de grano muy fino, capaz incluso de ocultar columnas concretas dejando visible el resto de la fila. Y tiene un requisito que la acompaña siempre: alguien con el conjunto de datos completo tiene que evaluar ese predicado, porque el cliente ni tiene los datos para hacerlo ni sería creíble que lo hiciera.
Con documentos, el permiso se ata a la identidad del documento: una lista de acceso o, en los sistemas cifrados, sencillamente una clave. Es de grano grueso y no admite matices dentro de la pieza, pero tiene una propiedad que ninguna cantidad de predicados alcanza: no necesita evaluador. Quien tiene la clave lee y quien no la tiene no lee, sin que nadie tenga que estar disponible para decidirlo, sin que el intermediario entienda el contenido y sin confiar en la infraestructura. Esa es la razón de fondo por la que el extremo descentralizado del eje anterior y el extremo documental de este aparecen juntos una y otra vez, y no es una coincidencia de mercado sino una implicación.
La consecuencia práctica más citada es la del campo sensible. Ocultar el salario a quien puede ver el resto de la ficha es trivial cuando la unidad es la fila, porque basta con no incluir esa columna en la proyección autorizada. Cuando la unidad es el documento, esa misma exigencia obliga a partir la ficha en dos documentos con permisos distintos, con lo que el requisito de control de acceso ha modificado el modelo de datos. No es un defecto del enfoque documental: es la manifestación directa de que la unidad de permiso y la unidad de sincronización son el mismo objeto, y por tanto todo cambio en una es un cambio en la otra.
La discusión sobre este eje suele degenerar en preferencias sobre bases de datos, y esa es exactamente la conversación equivocada, porque el eje no habla de cómo guardas nada: habla de si la granularidad de sincronización, la de permiso y la de conflicto son una sola o son tres independientes. En un sistema de documentos van soldadas por construcción; en uno de filas van sueltas, y puedes sincronizar por fila, permitir por predicado y resolver conflictos por columna sin que ninguna de las tres decisiones condicione a las otras. Enunciado así, parece obvio que soltar es mejor, porque más grados de libertad siempre suenan a mejor diseño. Y ahí está la trampa que ordena todo el nivel: cada grado de libertad que ganas exige a cambio un evaluador. Un predicado no se aplica solo. Alguien con el conjunto de datos completo, con identidad verificada y con disponibilidad tiene que ejecutar la regla para decidir qué filas te corresponden, y ese alguien es un servidor, con todo lo que eso arrastra —tiene que estar levantado, tiene que ser confiable, tiene que ver los datos en claro—. Soldar las tres granularidades, en cambio, permite sustituir la evaluación por posesión: una identidad única puede llevar asociada una clave única, y entonces el permiso deja de ser una regla que alguien comprueba para convertirse en un hecho criptográfico que nadie necesita comprobar. De ahí se deduce, sin haber mirado todavía un solo producto, algo que en la quinta lección aparecerá como una casilla vacía de la rejilla: un sistema verdaderamente descentralizado difícilmente puede ser de filas con permisos por predicado, porque estaría exigiendo una evaluación autoritativa que ha renunciado a tener. No es que nadie lo haya construido todavía por falta de tiempo o de talento; es que la combinación se contradice a sí misma. Y de ahí se deduce también el criterio de elección real, que no es de gusto: si tu control de acceso necesita expresar reglas que dependen del contenido cambiante de los datos, ya has elegido filas y ya has elegido servidor, aunque creas que la decisión sigue abierta. Si tu control de acceso se puede enunciar como quién pertenece a qué, tienes libertad genuina para elegir, y entonces sí conviene decidir por comodidad de modelado. Lo que no existe es la opción de tener predicados finos y no tener quien los evalúe.
- Elige las tres pantallas más usadas de tu producto y escribe qué filas exactas necesita cada una, con sus tablas y sus junturas.
- Reescribe ese mismo material como documentos y anota cuántos harían falta, cuál crecería sin techo y qué política de ciclo de vida necesitaría.
- Recorre tu código de cliente y localiza cada punto que da por hecho que una entidad relacionada está presente; conviértelo en un caso de ausencia explícito.
- Escribe tus reglas de acceso como predicados sobre filas y después como pertenencia a documentos, y marca las que no sobreviven a la segunda forma.
- Para cada regla que no sobrevive, dibuja la división en documentos que la haría expresable y valora si ese modelo sigue siendo razonable.
- Estima la contabilidad de sincronización de tu modelo en filas y compárala con el peso real del contenido para detectar la explosión de piezas diminutas.