How to Access and Understand the Core Workspace Entity in AFFiNE
Developers can access AFFiNE's core workspace entity by importing WorkspacesService and calling openWorkspace(), which returns a Workspace entity that exposes reactive metadata, document collections, and low-level engine services for real-time collaboration.
The core workspace in AFFiNE serves as the central abstraction for collaborative document management. Located in the toeverything/AFFiNE repository, this entity glues together metadata, Y-Doc state, and engine services to provide a unified API for building knowledge management applications.
Locating the Workspace Entity in the Source Code
Understanding where the workspace entity lives helps developers navigate the AFFiNE codebase effectively.
Core Entity Definition
The primary Workspace class resides in packages/frontend/core/src/modules/workspace/entities/workspace.ts. This file defines the entity that exposes id, meta, rootYDoc, and reactive streams like name$ and avatar$.
Supporting Architecture Files
The workspace architecture spans several key files:
packages/frontend/core/src/modules/workspace/scopes/workspace.ts– DefinesWorkspaceScope, which provides the runtimeopenOptionsand dependency-injection container for each workspace instance.packages/frontend/core/src/modules/workspace/impls/workspace.ts– ContainsWorkspaceImpl, the concrete implementation of the BlocksuiteWorkspaceinterface that wires Y-Doc with document creation and blob handling.packages/frontend/core/src/modules/workspace/services/engine.ts– HousesWorkspaceEngineService, providing low-level services fordoc,blob, andawarenessmanagement.
Understanding the Workspace Architecture
The Workspace entity acts as a façade that coordinates multiple subsystems for real-time collaboration.
Key Components Overview
The workspace integrates four critical components:
- Metadata – Stores workspace identity (
id,flavour) and user-defined properties (name,avatar). - Y-Doc – A
YDocinstance that serves as the collaborative state container, enabling real-time synchronization across clients. - Document Collection – Exposed via
docCollection, this provides the Blocksuite API for creating, retrieving, and managing individual documents. - Engine Services – Accessed through
engine, these handle low-level operations like blob storage, document syncing, and awareness protocols.
The Y-Doc Foundation
At the heart of every workspace lies a YDoc instance created with the workspace ID as its GUID. This Y-Doc stores:
- Workspace metadata in a Y-Map (
meta.name,meta.avatar) - Document references and state
- Binary blob references
The Workspace entity exposes reactive LiveData streams (name$, avatar$) that automatically update when the underlying Y-Map changes, enabling UI components to react to remote modifications in real time.
Accessing the Workspace Entity in Your Code
Developers interact with workspaces through the WorkspacesService, which acts as the entry point for workspace lifecycle management.
Opening a Workspace Instance
To access a workspace, inject WorkspacesService and call openWorkspace() with the workspace metadata:
import { WorkspacesService } from '@affine/core/modules/workspace/services/workspaces';
// Assuming a DI framework instance
const workspaces = framework.get(WorkspacesService);
// Open workspace by ID (typically from URL or user profile)
const workspace = await workspaces.openWorkspace({
id: 'workspace-123',
// additional metadata like flavour, name, etc.
});
The openWorkspace method internally creates a WorkspaceScope and instantiates the Workspace entity with all necessary dependencies.
Reading Reactive Properties
Once you have a workspace instance, subscribe to reactive properties for real-time UI updates:
// Subscribe to workspace name changes
workspace.name$.subscribe(name => {
console.log('Workspace name updated:', name);
});
// Subscribe to avatar changes
workspace.avatar$.subscribe(url => {
console.log('Avatar URL:', url);
});
These LiveData streams automatically reflect changes made by remote collaborators through the Y-Doc synchronization layer.
Modifying Workspace Metadata
Update workspace properties using the provided mutator methods, which wrap Y-JS transactions:
// Update workspace name
workspace.setName('Q4 Planning Documents');
// Update workspace avatar
workspace.setAvatar('https://cdn.example.com/team-avatar.png');
Both methods perform atomic Y-JS transactions that propagate to all connected clients immediately.
Working with Documents and Blobs
The workspace entity provides access to document management and binary storage capabilities.
Document Operations
Access the document collection through docCollection to create and manage documents:
// Create a new document
const newDoc = workspace.docs.createDoc({ id: 'my-new-doc' });
console.log('Created doc with ID:', newDoc.id);
// Access existing document via docCollection
const existingDoc = workspace.docCollection.getDoc('existing-doc-id');
// Connect document to engine for collaboration
workspace.engine.doc.connectDoc(existingDoc);
The docs service (DocsService) handles CRUD operations, while docCollection provides the Blocksuite-compatible interface.
Blob Storage
Upload and retrieve binary files through the blob API:
const file = new File([/* binary data */], 'diagram.png', {
type: 'image/png'
});
// Upload blob
workspace.docCollection.blob.set(file.name, file).then(blobId => {
console.log('Blob stored with ID:', blobId);
});
// Retrieve blob
const retrievedBlob = await workspace.docCollection.blob.get(blobId);
The blob operations delegate to WorkspaceEngineService, which abstracts the underlying storage mechanism (IndexedDB, cloud sync, etc.).
Summary
- The core workspace entity in AFFiNE is the
Workspaceclass located atpackages/frontend/core/src/modules/workspace/entities/workspace.ts. - Access workspaces through
WorkspacesService.openWorkspace(), which returns a fully initialized entity with reactive metadata streams. - The architecture separates concerns between the entity (high-level API), engine (low-level services), and scope (dependency injection).
- Real-time collaboration relies on a central Y-Doc instance exposed via
rootYDoc, with reactiveLiveDatastreams (name$,avatar$) for UI synchronization. - Document and blob operations flow through
docCollectionandengineservices, providing Blocksuite-compatible APIs for content management.
Frequently Asked Questions
What is the difference between Workspace and WorkspaceImpl in AFFiNE?
The Workspace class in entities/workspace.ts serves as the high-level entity façade that exposes reactive metadata streams and coordinates between subsystems. WorkspaceImpl in impls/workspace.ts implements the Blocksuite Workspace interface, handling the concrete Y-Doc wiring, blob APIs, and document collection mechanics. Think of Workspace as the developer-facing API and WorkspaceImpl as the Blocksuite adapter.
How does AFFiNE handle real-time collaboration at the workspace level?
Real-time collaboration centers on the YDoc instance stored in rootYDoc. When you call setName() or setAvatar(), the entity executes Y-JS transactions that modify the underlying Y-Map. These changes propagate through the WorkspaceEngineService, which manages awareness protocols and document synchronization. UI components subscribe to LiveData streams (name$, avatar$) to reflect remote updates instantly without manual polling.
Where should I look to understand workspace metadata storage?
Workspace metadata flows through multiple layers. Start with packages/frontend/core/src/modules/workspace/entities/workspace.ts to see how meta, name$, and avatar$ expose the data. The actual storage mechanism resides in the Y-Doc's Y-Map, managed through WorkspaceImpl. For persistence and synchronization logic, examine packages/frontend/core/src/modules/workspace/services/engine.ts, which coordinates how metadata changes sync across clients.
Can I mock the workspace entity for unit testing?
Yes. Because AFFiNE uses dependency injection, you can mock the WorkspaceScope and WorkspaceEngineService when instantiating the Workspace entity. Create a test double for WorkspaceScope that provides fake openOptions metadata, and mock the engine's doc, blob, and awareness services. Since the entity exposes reactive LiveData streams, you can also mock these observables to return synchronous test values, enabling isolated unit tests without Y-Doc or network dependencies.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →