architect/research/DOCS_IMPLEMENTATION_PATTERNS.md

Паттерны реализации систем управления документами

Дата: 2026-01-02
Статус: Практические выводы
Уровень: Архитектор


ТАБЛИЦА РЕШЕНИЙ: Встроенное vs Ссылки

Матрица выбора

                    ЧАСТО МЕНЯЕТСЯ    РЕДКО МЕНЯЕТСЯ
                    ↓                  ↓
ВНЕШНИЙ ИСТОЧНИК    Ссылка/Macro      Ссылка (с cache)
                    (fetch on render)  (fetch on deploy)

НАША СИСТЕМА        Встроено          Встроено
                    (copy on import)   (snapshot)

Примеры из практики

Случай 1: JIRA issues в Confluence

JIRA (external system with changing data)
     Reference via macro
Confluence page: jira-issue(key=PROJ-123)
     On render (dynamic)
User sees: Latest issue status, assignee, etc.

Почему macro (ссылка)?
- JIRA обновляется часто
- Confluence не должна содержать копию
- При render → fetch latest state


Случай 2: Product prices в каталоге

SCENARIO A: Часто меняются цены
    Price source (external)
        ↓ API on render
    Webpage shows: Latest price

SCENARIO B: Fixed pricing для документации
    Copy into document (embed)
        ↓ Manual sync (or CI)
    Document shows: Price "frozen" at publish time

Выбор:
- A: Real-time → Links/API
- B: Historical → Embed


Паттерн 1: Embedded (Встроено)

Когда:
- Контент редко меняется
- Нужна независимость от источника
- Performance критична (нет extra requests)
- Документ должен быть "frozen" на момент публикации

Реализация:

1. Source  Document (import/copy)
2. Store in DB
3. On render  Show stored version
4. Update  Manual trigger or scheduled import

Example: GitBook
├── Source: Git repository
├── Storage: GitBook DB + Git (synced)
├── Render: Stored markdown
└── Update: On push to Git

Плюсы:
- Быстро (нет extra requests)
- Independent (не зависит от external)
- Versioned (история всех versions)
- Clear attribution (видно что и когда импортировали)

Минусы:
- Outdated risk (если source изменится)
- Data duplication
- Sync overhead


Паттерн 2: Linked (Ссылка)

Когда:
- Контент часто меняется
- Нужны всегда актуальные данные
- External source достаточно стабилен
- Performance не критична

Реализация:

1. Document contains reference (URL, ID, macro)
2. On render: Fetch from source
3. Show fetched content to user
4. Caching: Optional (для performance)

Example: Confluence macro
├── Page: jira-issue(key=PROJ-123)
├── On render: Fetch from JIRA API
├── Show: Live issue data
└── Update: Automatic (next page load)

Плюсы:
- Always fresh (latest data)
- No duplication
- No sync problems
- Single source of truth

Минусы:
- Slower (extra API call)
- Depends on external (downtime risk)
- No offline access
- Version history complex


Паттерн 3: Hybrid (Git + Embedded)

Рекомендуемый паттерн для документ-систем.

Концепция:

Git Repository
(Source of truth, versioned)
    
    ├─→ [Web server]  Render & Cache
                      Show to user
    ├─→ [Database]  Metadata, search index
    └─→ [Export]  Markdown, HTML, JSON

Преимущества:

Аспект Выгода
Версионирование Git commits (полная история)
Portability Markdown в git (export anytime)
Collaboration Git workflow + UI edits
Backup Git is inherent backup
Performance Cache on server
Independence Not tied to one platform

Реализация: GitBook approach

.gitbook.yaml
├─ github:
    owner: org
    repo: docs
    branch: main
└─ publish: on-merge

Workflow:
1. Edit in GitBook UI
   
2. Save  auto-commit to GitHub
   
3. CI/CD pulls latest
   
4. Render & cache
   
5. Serve to users

Sync strategy (2-way):

GitBook UI change
    ↓ Save
Git commit
    ↓ Pull
Web server refresh
    ↓
User sees latest

ПАТТЕРНЫ ХРАНЕНИЯ

Паттерн A: Document = Collections of blocks

Используют: Notion, Coda

Document
├── Block 1 (type: paragraph)
   ├── id: UUID
   ├── type: "paragraph"
   ├── content: RichText[]
   ├── properties: {}
   └── parent_id: UUID
├── Block 2 (type: heading_1)
   └── ...
├── Block N (type: database_page)
   └── Fields: [field1, field2, ...]

Хранение:

blocks table:
  id UUID PRIMARY KEY
  parent_id UUID (nullable)
  type VARCHAR
  properties JSONB
  created_by UUID
  edited_by UUID
  created_at TIMESTAMP
  updated_at TIMESTAMP
  position INTEGER (for ordering)

Плюсы:
- Гибкость (трансформация блоков)
- Granular versioning
- Nested structure
- Rich metadata

Минусы:
- Сложнее в query (много JOINs)
- Performance on huge documents
- Complex consistency checks


Паттерн B: Document = Hierarchical pages

Используют: Confluence, traditional wikis

Document (Space)
├── Page 1
   ├── title: str
   ├── body: XHTML
   ├── metadata: {}
   └── attachments: [File]
├── Page 2
   └── children: [Page 3, Page 4]
├── Page 3
└── Page 4

Хранение:

pages table:
  id BIGINT PRIMARY KEY
  space_id INT
  parent_id BIGINT (nullable)
  title VARCHAR
  body CLOB (XHTML)
  created_by INT
  updated_by INT
  created_date TIMESTAMP
  updated_date TIMESTAMP

attachments table:
  id INT
  page_id BIGINT
  filename VARCHAR
  data BLOB

comments table:
  id INT
  page_id BIGINT
  body XHTML
  author INT

Плюсы:
- Простая иерархия (tree)
- Clear permissions per page
- Easy to archive
- Efficient for document organization

Минусы:
- Comments as separate entities
- XHTML not portable
- Less granular tracking


Паттерн C: Document = Markdown files + folder structure

Используют: GitBook, Obsidian, GitHub Pages

Repository
├── README.md (root page)
├── docs/
│   ├── guide/
│   │   ├── index.md
│   │   ├── getting-started.md
│   │   └── advanced.md
│   ├── api/
│   │   ├── overview.md
│   │   └── endpoints.md
│   └── faq.md
├── .gitbook.yaml (or jekyll config)
└── assets/
    └── images/

Хранение:

File system (Git repository)
├── Markdown files
├── Binary assets
└── Config files (YAML)

Database (optional):
├── Metadata (search index)
├── Comments
├── Permissions
└── Analytics

Плюсы:
- Portable (plain markdown)
- Version control (git commits)
- Easy backup
- Works with any text editor
- Portability

Минусы:
- Comments not in markdown (separate DB)
- Search requires indexing
- No real-time collaboration (depends on sync)


РЕАЛИЗАЦИЯ: Шаг за шагом

Step 1: Выбрать storage

Вопрос 1: Контент часто меняется?
├─ ДА  → Паттерн 2 (Links)
└─ НЕТ → Паттерн 1 (Embedded)

Вопрос 2: Нужна portability?
├─ ДА  → Паттерн C (Markdown)
└─ НЕТ → Паттерн A или B

Вопрос 3: Нужна full versioning?
├─ ДА  → Паттерн 3 (Git + Embedded)
└─ НЕТ → Паттерн A (Blocks)

Step 2: Выбрать структуру данных

Выбор Хранилище Структура
Максимальная гибкость PostgreSQL Blocks (A)
Традиционные docs Database Pages (B)
Максимальная portability Git Markdown (C)
Recommended для нового Git + DB Hybrid (C + metadata)

Step 3: Спроектировать API

Blocks API (Notion-like)

GET    /documents/{id}/blocks
POST   /documents/{id}/blocks
PATCH  /blocks/{block_id}
DELETE /blocks/{block_id}

GET    /blocks/{block_id}/children

Pages API (Confluence-like)

GET    /spaces/{space_id}/pages
POST   /spaces/{space_id}/pages
PATCH  /pages/{page_id}
GET    /pages/{page_id}/children
GET    /pages/{page_id}/comments

Documents API (GitBook-like)

GET    /documents/{id}
GET    /documents/{id}/pages
PATCH  /documents/{id}/pages/{page_id}
GET    /documents/{id}/revisions

Step 4: Спроектировать editor

Option 1: Block-based WYSIWYG

class BlockEditor {
  blocks: Block[];

  addBlock(type: string, position: number);
  removeBlock(id: string);
  updateBlock(id: string, content: unknown);
  transformBlock(id: string, newType: string);

  // Collaboration
  onRemoteChange(change: Change);
  publishChange(change: Change);
}

Option 2: Markdown + WYSIWYG split

class DocumentEditor {
  modes: 'wysiwyg' | 'markdown' | 'split';

  // WYSIWYG side
  blocks: Block[];

  // Markdown side
  source: string;

  syncWYSIWYG2Markdown();
  syncMarkdown2WYSIWYG();
}

Step 5: Real-time collaboration

Conflict-free approach (CRDTs):

User A changes block 1
User B changes block 2

Both changes merge automatically
(no conflict - different blocks)

WebSocket protocol:

Client → Server: {action: "update_block", id: "...", content: "..."}
Server → All clients: {action: "block_updated", ...}

КОМБО: Git + Block Editor + Real-time

Архитектура

┌─────────────────────────────────────────────┐
│         Document Management System           │
└─────────────────────────────────────────────┘
                    ↓
┌──────────────────────────────────────────────────┐
│         Storage Layer                             │
├──────────────────────────────────────────────────┤
│ Primary: Git repository (markdown)                │
│ Secondary: PostgreSQL (metadata, blocks, collab) │
│ Tertiary: S3/local (binary assets)              │
└──────────────────────────────────────────────────┘
                    ↓
┌──────────────────────────────────────────────────┐
│         Sync Layer                                │
├──────────────────────────────────────────────────┤
│ Git ↔ DB (bidirectional):                        │
│   - On Git push → update DB metadata              │
│   - On UI change → commit to Git                  │
│ Conflict resolution: Last-write-wins              │
└──────────────────────────────────────────────────┘
                    ↓
┌──────────────────────────────────────────────────┐
│         API Layer                                 │
├──────────────────────────────────────────────────┤
│ REST: CRUD documents, blocks, pages               │
│ WebSocket: Real-time collaboration               │
│ Webhooks: External integrations                  │
└──────────────────────────────────────────────────┘
                    ↓
┌──────────────────────────────────────────────────┐
│         Editor Layer (Frontend)                   │
├──────────────────────────────────────────────────┤
│ Block-based editor (WYSIWYG)                     │
│ Markdown source view                             │
│ Real-time cursor tracking                        │
│ Comments/discussions                             │
└──────────────────────────────────────────────────┘

РЕШЕНИЕ ПРОБЛЕМ

Проблема 1: Large documents slow down

Решение:

 Load entire document
   Document.blocks.length = 10,000
   Render: O(n) = slow

 Lazy load + virtualization
   Load only visible blocks
   Render: O(visible) = fast

 Split into pages
   Document  Pages
   Each page ~200 blocks
   Load on demand

Проблема 2: Collaboration conflicts

Решение: Operational Transform or CRDT

User A:  Insert "hello" at pos 0
         Document: "hello world"

User B:  Delete char at pos 0
         Document: "ello world"

Conflict?
  A sees: "hello world"
  B sees: "ello world"

Solution (CRDT): Assign unique IDs to chars
  A inserts: {id: A-1, char: 'h', after: null}
  B deletes: {id: A-2} ← knows which char

Result: Both converge to "ello world"

Проблема 3: Versioning with real-time edits

Решение: Git-based versioning

Real-time edits in memory/DB
    ↓ Every N seconds or on explicit save
Commit to Git
    ↓
Full history preserved
    ↓
Can rollback anytime

Проблема 4: Search in large repository

Решение: Full-text index

Documents  Extract text
          Index (Elasticsearch, PostgreSQL FTS)
          Query
          Return results with highlighting

Example (PostgreSQL FTS):

CREATE INDEX documents_search_idx
ON documents USING GIN(to_tsvector('english', content));

SELECT * FROM documents
WHERE to_tsvector('english', content) @@ to_tsquery('english', 'search term')
ORDER BY ts_rank(...);

CHECKLIST: Evaluation Матрица

Перед выбором системы/паттерна:

Функциональность

Масштабируемость

Архитектура

Операционная готовность


ИТОГОВЫЕ РЕКОМЕНДАЦИИ

Для личного knowledge base

Obsidian (Markdown files + local + optional sync)

Причины:
- Полная контроль (local files)
- Портативность (markdown)
- Extensible (plugins)
- Cheap ($0-48/год)

Для документации команды

GitBook (Git + Markdown)

Причины:
- Git integration (versioning)
- Markdown (portable)
- Pretty publishing
- Easy collaboration (Git flow)

Для enterprise documentation

Confluence (Page hierarchy + XHTML + macros)

Причины:
- Enterprise features (permissions, SSO)
- Macros (JIRA, Trello integration)
- Full-text search
- Built-in comments

Для database + documents hybrid

Notion (Blocks + Databases + Automation)

Причины:
- Unified platform (docs + db)
- Automation (automation engine)
- Flexible (blocks)
- Great for teams

Для lightweight team workspace

Coda (Canvas + Tables + Automations)

Причины:
- Interactive (buttons, automations)
- Real-time collaboration
- Lightweight than Notion

Конец документа