🏗️
Arquitectura de Sincronización — NoteFlow CRDT Stack
Offline-First
Ana García (PM)
Chrome / MacBook
Y.Doc
Tiptap
IndexedDB
Carlos Ruiz (Dev)
Firefox / Linux
Y.Doc
Tiptap
IndexedDB
Laura Sánchez (UX)
Safari / iPhone — OFFLINE
Y.Doc
Tiptap
IndexedDB ✓
Marcos Torres (CTO)
Edge / Windows
Y.Doc
Tiptap
IndexedDB
WSS :443
y-websocket
y-websocket
Broadcast
CRDT updates
CRDT updates
y-websocket Server
Node.js · Puerto 443 (WSS)
setupWSConnection + auth JWT
setPersistence → MongoDB Atlas
flushSize: 100 updates / batch
Awareness: cursores + presencia
💻
Implementación — Archivos Clave de NoteFlow
TypeScript · React
CollaborativeEditor.tsx
provider.ts
yjs-server.ts
offline.ts
// src/components/CollaborativeEditor.tsx — NoteFlow editor con presencia import { useEditor, EditorContent } from "@tiptap/react"; import Collaboration from "@tiptap/extension-collaboration"; import CollaborationCursor from "@tiptap/extension-collaboration-cursor"; import * as Y from "yjs"; import { WebsocketProvider } from "y-websocket"; import { IndexeddbPersistence } from "y-indexeddb"; const NOTEFLOW_USERS = { ana: { name: "Ana García", color: "#4F46E5" }, carlos: { name: "Carlos Ruiz", color: "#0EA5E9" }, laura: { name: "Laura Sánchez", color: "#10B981" }, marcos: { name: "Marcos Torres", color: "#F59E0B" }, }; export function CollaborativeEditor({ documentId, userId }: Props) { // 1. Documento CRDT compartido entre todos los clientes const doc = useMemo(() => new Y.Doc(), []); // 2. Persistencia offline en IndexedDB (1 línea) const _ = useMemo(() => new IndexeddbPersistence(`noteflow-${documentId}`, doc), [doc, documentId]); // 3. WebSocket provider (solo WSS :443, sin WebRTC) const provider = useMemo(() => new WebsocketProvider( "wss://collab.noteflow.app", documentId, doc, { params: { token: getAuthToken() } } ), [doc, documentId]); // 4. Awareness: nombre + color del cursor visible para otros useEffect(() => { provider.awareness.setLocalStateField("user", NOTEFLOW_USERS[userId]); }, [provider, userId]); // 5. Editor Tiptap con CRDT (sin history nativo → Yjs lo gestiona) const editor = useEditor({ extensions: [ StarterKit.configure({ history: false }), // ← CRÍTICO Collaboration.configure({ document: doc }), CollaborationCursor.configure({ provider, user: NOTEFLOW_USERS[userId] }), ], }); // Cleanup al desmontar useEffect(() => () => { provider.destroy(); doc.destroy(); }, [doc, provider]); return ( <div className="editor-wrapper"> <PresenceBar provider={provider} /> <EditorContent editor={editor} /> <ConnectionBadge provider={provider} /> </div> ); }
⚡
Resolución Automática de Conflictos — Simulación CRDT
Sin pérdida de datos
A
t=0ms — Estado inicial (todos sincronizados)
Todos los clientes tienen el mismo documento. Ana y Carlos empiezan a editar en paralelo sin conexión entre sí (Laura se ha desconectado).
"El objetivo de NoteFlow es simplificar la colaboración."
C
t=120ms — Carlos inserta "equipos distribuidos" (posición 42)
Carlos añade texto en el centro de la frase. Su Y.Doc local registra la operación con vector clock único.
"El objetivo de NoteFlow es simplificar la colaboración de equipos distribuidos."
A
t=185ms — Ana inserta "en tiempo real" (posición 47, concurrent)
Ana, sin recibir aún el update de Carlos, edita la misma zona. CRDT asigna un identificador de posición único (lamport timestamp + clientID).
"El objetivo de NoteFlow es simplificar la colaboración en tiempo real."
✓
t=210ms — Servidor aplica ambas operaciones (merge determinista)
Y.applyUpdate() fusiona los dos vectores. Como las inserciones tienen posiciones diferentes (identificadores únicos), ambas se preservan. Orden determinado por clientID: Ana (4f46) < Carlos (0ea5).
"El objetivo de NoteFlow es simplificar la colaboración de equipos distribuidos en tiempo real."
L
t=8min — Laura reconecta (había editado offline)
Laura añadió "de forma asíncrona" mientras estaba offline. IndexedDB tenía su estado local. Al reconectar, y-websocket sincroniza automáticamente su Y.Doc con el servidor. El merge CRDT preserva todos los cambios sin intervención manual.
"El objetivo de NoteFlow es simplificar la colaboración de equipos distribuidos en tiempo real de forma asíncrona."
✅ Resultado: 0 conflictos, 0 pérdidas de datos. Los 3 usuarios ven exactamente el mismo documento final. Laura no tuvo que "resolver conflictos" manualmente. Esto es lo que reemplaza el 23% de tickets de soporte de NoteFlow.