# How to Access and Understand the Core Workspace Entity in AFFiNE

> Developers access AFFiNE's core workspace entity via WorkspacesService openWorkspace(). Get reactive metadata, document collections, and engine services for real time collaboration.

- Repository: [Toeverything/AFFiNE](https://github.com/toeverything/AFFiNE)
- Tags: internals
- Published: 2026-03-05

---

**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`](https://github.com/toeverything/AFFiNE/blob/main/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`](https://github.com/toeverything/AFFiNE/blob/main/packages/frontend/core/src/modules/workspace/scopes/workspace.ts)** – Defines `WorkspaceScope`, which provides the runtime `openOptions` and dependency-injection container for each workspace instance.
- **[`packages/frontend/core/src/modules/workspace/impls/workspace.ts`](https://github.com/toeverything/AFFiNE/blob/main/packages/frontend/core/src/modules/workspace/impls/workspace.ts)** – Contains `WorkspaceImpl`, the concrete implementation of the Blocksuite `Workspace` interface that wires Y-Doc with document creation and blob handling.
- **[`packages/frontend/core/src/modules/workspace/services/engine.ts`](https://github.com/toeverything/AFFiNE/blob/main/packages/frontend/core/src/modules/workspace/services/engine.ts)** – Houses `WorkspaceEngineService`, providing low-level services for `doc`, `blob`, and `awareness` management.

## 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:

1. **Metadata** – Stores workspace identity (`id`, `flavour`) and user-defined properties (`name`, `avatar`).
2. **Y-Doc** – A `YDoc` instance that serves as the collaborative state container, enabling real-time synchronization across clients.
3. **Document Collection** – Exposed via `docCollection`, this provides the Blocksuite API for creating, retrieving, and managing individual documents.
4. **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:

```typescript
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:

```typescript
// 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:

```typescript
// 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:

```typescript
// 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:

```typescript
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 `Workspace` class located at [`packages/frontend/core/src/modules/workspace/entities/workspace.ts`](https://github.com/toeverything/AFFiNE/blob/main/packages/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 reactive `LiveData` streams (`name$`, `avatar$`) for UI synchronization.
- Document and blob operations flow through **`docCollection`** and **`engine`** services, 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`](https://github.com/toeverything/AFFiNE/blob/main/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`](https://github.com/toeverything/AFFiNE/blob/main/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`](https://github.com/toeverything/AFFiNE/blob/main/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`](https://github.com/toeverything/AFFiNE/blob/main/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.