|
1 | 1 | # First Principles |
2 | 2 |
|
3 | | -This document outlines the fundamental principles that guide the design of `Elastic.Documentation.Navigation`. |
| 3 | +The navigation system is built on two types of principles: |
4 | 4 |
|
5 | | -## Core Principles |
| 5 | +## [Functional Principles](functional-principles.md) |
6 | 6 |
|
7 | | -### 1. Two-Phase Loading |
| 7 | +These define **what** the navigation system does and **why**: |
8 | 8 |
|
9 | | -Navigation construction follows a strict two-phase approach: |
| 9 | +1. **Two-Phase Loading** - Separate configuration resolution from navigation construction |
| 10 | +2. **Single Documentation Source** - All paths relative to docset root |
| 11 | +3. **URL Building is Dynamic** - URLs calculated on-demand, not stored |
| 12 | +4. **Navigation Roots Can Be Re-homed** - O(1) URL prefix changes |
| 13 | +5. **Navigation Scope via HomeProvider** - Scoped URL calculation contexts |
| 14 | +6. **Index Files Determine URLs** - Folders and indexes share URLs |
| 15 | +7. **File Structure Mirrors Navigation** - Predictable, maintainable structure |
| 16 | +8. **Acyclic Graph Structure** - Tree with no cycles, unique URLs |
| 17 | +9. **Phantom Nodes** - Acknowledge content without including it |
10 | 18 |
|
11 | | -**Phase 1: Configuration Resolution** (`Elastic.Documentation.Configuration`) |
12 | | -- Parse YAML files (`docset.yml`, `toc.yml`, `navigation.yml`) |
13 | | -- Resolve all file references to **full paths** relative to documentation set root |
14 | | -- Validate configuration structure and relationships |
15 | | -- Output: Fully resolved configuration objects with complete file paths |
| 19 | +[Read Functional Principles →](functional-principles.md) |
16 | 20 |
|
17 | | -**Phase 2: Navigation Construction** (`Elastic.Documentation.Navigation`) |
18 | | -- Consume resolved configuration from Phase 1 |
19 | | -- Build navigation tree with **full URLs** |
20 | | -- Create node relationships (parent/child/root) |
21 | | -- Set up home providers for URL calculation |
22 | | -- Output: Complete navigation tree with calculated URLs |
| 21 | +## [Technical Principles](technical-principles.md) |
23 | 22 |
|
24 | | -**Why Two Phases?** |
25 | | -- **Separation of Concerns**: Configuration parsing is independent of navigation structure |
26 | | -- **Validation**: Catch file/structure errors before building expensive navigation trees |
27 | | -- **Reusability**: Same configuration can build different navigation structures (isolated vs assembler) |
28 | | -- **Performance**: Resolve file system operations once, reuse for navigation |
| 23 | +These define **how** the navigation system is implemented: |
29 | 24 |
|
30 | | -### 2. Single Documentation Source |
| 25 | +1. **Generic Type System** - Covariance enables static typing without runtime casts |
| 26 | +2. **Provider Pattern** - Decouples URL calculation from tree structure |
| 27 | +3. **Lazy URL Calculation** - Smart caching with automatic invalidation |
31 | 28 |
|
32 | | -URLs are always built relative to the documentation set's source directory: |
33 | | -- Files referenced in `docset.yml` are relative to the docset root |
34 | | -- Files referenced in nested `toc.yml` are relative to the toc directory |
35 | | -- During Phase 1, all paths are resolved to be relative to the docset root |
36 | | -- During Phase 2, URLs are calculated from these resolved paths |
| 29 | +[Read Technical Principles →](technical-principles.md) |
37 | 30 |
|
38 | | -**Example:** |
39 | | -``` |
40 | | -docs/ |
41 | | -├── docset.yml # Root |
42 | | -├── index.md |
43 | | -└── api/ |
44 | | - ├── toc.yml # Nested TOC |
45 | | - └── rest.md |
46 | | -``` |
| 31 | +--- |
47 | 32 |
|
48 | | -Phase 1 resolves `api/toc.yml` reference to `rest.md` as: `api/rest.md` (relative to docset root) |
49 | | -Phase 2 builds URL as: `/api/rest/` |
| 33 | +## Quick Reference |
50 | 34 |
|
51 | | -### 3. URL Building is Dynamic and Cheap |
| 35 | +**For understanding architecture:** Start with [Functional Principles](functional-principles.md) |
52 | 36 |
|
53 | | -URLs are **calculated on-demand**, not stored: |
54 | | -- Nodes don't store their final URL |
55 | | -- URLs are computed from `HomeProvider.PathPrefix` + relative path |
56 | | -- Changing a `HomeProvider` instantly updates all descendant URLs |
57 | | -- No tree traversal needed to update URLs |
| 37 | +**For implementation details:** See [Technical Principles](technical-principles.md) |
58 | 38 |
|
59 | | -**Why Dynamic?** |
60 | | -- **Re-homing**: Same subtree can have different URLs in different contexts |
61 | | -- **Memory Efficient**: Don't store redundant URL strings |
62 | | -- **Consistency**: URLs always reflect current home provider state |
| 39 | +**For visual examples:** See [Visual Walkthrough](visual-walkthrough.md) |
63 | 40 |
|
64 | | -### 4. Navigation Roots Can Be Re-homed |
65 | | - |
66 | | -A key design feature that enables assembler builds: |
67 | | -- **Isolated Build**: Each `DocumentationSetNavigation` is its own root |
68 | | -- **Assembler Build**: `SiteNavigation` becomes the root, docsets are "re-homed" |
69 | | -- **Re-homing**: Replace a subtree's `HomeProvider` to change its URL prefix |
70 | | -- **Cheap Operation**: O(1) - just replace the provider reference |
71 | | - |
72 | | -**Example:** |
73 | | -```csharp |
74 | | -// Isolated: URLs start at / |
75 | | -homeProvider.PathPrefix = ""; |
76 | | -// → /api/rest/ |
77 | | -
|
78 | | -// Assembled: Re-home to /guide |
79 | | -homeProvider = new NavigationHomeProvider("/guide", siteNav); |
80 | | -// → /guide/api/rest/ |
81 | | -``` |
82 | | - |
83 | | -### 5. Navigation Scope via HomeProvider |
84 | | - |
85 | | -`INavigationHomeProvider` creates navigation scopes: |
86 | | -- **Provider**: Defines `PathPrefix` and `NavigationRoot` for a scope |
87 | | -- **Accessor**: Children use `INavigationHomeAccessor` to access their scope |
88 | | -- **Inheritance**: Child nodes inherit their parent's accessor |
89 | | -- **Isolation**: Changes to a provider only affect its scope |
90 | | - |
91 | | -**Scope Creators:** |
92 | | -- `DocumentationSetNavigation` - Creates scope for entire docset |
93 | | -- `TableOfContentsNavigation` - Creates scope for TOC subtree (enables re-homing) |
94 | | - |
95 | | -**Scope Consumers:** |
96 | | -- `FileNavigationLeaf` - Uses accessor to calculate URL |
97 | | -- `FolderNavigation` - Passes accessor to children |
98 | | -- `VirtualFileNavigation` - Passes accessor to children |
99 | | - |
100 | | -### 6. Index Files Determine Folder URLs |
101 | | - |
102 | | -Every folder/node navigation has an **Index**: |
103 | | -- Index is either `index.md` or the first file |
104 | | -- The node's URL is the same as its Index's URL |
105 | | -- Children appear "under" the index in navigation |
106 | | -- Index files map to folder paths: `/api/index.md` → `/api/` |
107 | | - |
108 | | -**Why?** |
109 | | -- **Consistent URL Structure**: Folders and their indexes share the same URL |
110 | | -- **Natural Navigation**: Index represents the folder's landing page |
111 | | -- **Hierarchical**: Clear parent-child URL relationships |
112 | | - |
113 | | -### 7. File Structure Should Mirror Navigation |
114 | | - |
115 | | -Best practices for maintainability: |
116 | | -- Navigation structure should follow file system structure |
117 | | -- Avoid deep-linking files from different directories |
118 | | -- Use `folder:` references when possible |
119 | | -- Virtual files should group sibling files, not restructure the tree |
120 | | - |
121 | | -**Rationale:** |
122 | | -- **Discoverability**: Developers can find files by following navigation |
123 | | -- **Predictability**: URL structure matches file structure |
124 | | -- **Maintainability**: Moving files in navigation matches moving them on disk |
125 | | - |
126 | | -### 8. Generic Type System for Covariance |
127 | | - |
128 | | -Navigation classes are generic over `TModel`: |
129 | | -```csharp |
130 | | -public class DocumentationSetNavigation<TModel> |
131 | | - where TModel : class, IDocumentationFile |
132 | | -``` |
133 | | - |
134 | | -**Why Generic?** |
135 | | -- **Covariance**: Can treat `DocumentationSetNavigation<MarkdownFile>` as `IRootNavigationItem<IDocumentationFile, INavigationItem>` |
136 | | -- **Type Safety**: Factory pattern ensures correct model types |
137 | | -- **Flexibility**: Same navigation code works with different file models |
138 | | - |
139 | | -### 9. Lazy URL Calculation with Caching |
140 | | - |
141 | | -`FileNavigationLeaf` implements smart URL caching: |
142 | | -```csharp |
143 | | -private string? _homeProviderCache; |
144 | | -private string? _urlCache; |
145 | | - |
146 | | -public string Url |
147 | | -{ |
148 | | - get |
149 | | - { |
150 | | - if (_homeProviderCache == HomeProvider.Id && _urlCache != null) |
151 | | - return _urlCache; |
152 | | - |
153 | | - _urlCache = CalculateUrl(); |
154 | | - _homeProviderCache = HomeProvider.Id; |
155 | | - return _urlCache; |
156 | | - } |
157 | | -} |
158 | | -``` |
159 | | - |
160 | | -**Strategy:** |
161 | | -- Cache URL along with HomeProvider ID |
162 | | -- Invalidate cache when HomeProvider changes |
163 | | -- Recalculate only when needed |
164 | | -- O(1) for repeated access, O(path) for calculation |
165 | | - |
166 | | -### 10. Phantom Nodes for Incomplete Navigation |
167 | | - |
168 | | -`navigation.yml` can declare phantoms: |
169 | | -```yaml |
170 | | -phantoms: |
171 | | - - source: plugins:// |
172 | | -``` |
173 | | -
|
174 | | -**Purpose:** |
175 | | -- Reference nodes that exist but aren't included in site navigation |
176 | | -- Prevent "undeclared navigation" warnings |
177 | | -- Document intentionally excluded content |
178 | | -- Enable validation of cross-links |
179 | | -
|
180 | | -## Design Patterns |
181 | | -
|
182 | | -### Factory Pattern for Node Creation |
183 | | -
|
184 | | -`DocumentationNavigationFactory` creates navigation items: |
185 | | -- Encapsulates construction logic |
186 | | -- Ensures consistent initialization |
187 | | -- Centralizes node creation |
188 | | -- Type-safe generic methods |
189 | | - |
190 | | -### Provider Pattern for URL Context |
191 | | - |
192 | | -`INavigationHomeProvider` / `INavigationHomeAccessor`: |
193 | | -- Providers define context (PathPrefix, NavigationRoot) |
194 | | -- Accessors reference providers |
195 | | -- Decouples URL calculation from tree structure |
196 | | -- Enables context switching (re-homing) |
197 | | - |
198 | | -### Visitor Pattern for Tree Operations |
199 | | - |
200 | | -Navigation items implement interfaces for traversal: |
201 | | -- `IRootNavigationItem<TModel, TChild>` - Root nodes |
202 | | -- `INodeNavigationItem<TModel, TChild>` - Nodes with children |
203 | | -- `ILeafNavigationItem<TModel>` - Leaf nodes |
204 | | -- Common `INavigationItem` base for polymorphic traversal |
205 | | - |
206 | | -## Key Invariants |
207 | | - |
208 | | -1. **Phase Order**: Configuration must be fully resolved before navigation construction |
209 | | -2. **Path Resolution**: All paths in configuration are relative to docset root after Phase 1 |
210 | | -3. **URL Uniqueness**: Every navigation item must have a unique URL within its site |
211 | | -4. **Root Consistency**: All nodes in a subtree point to the same `NavigationRoot` |
212 | | -5. **Provider Validity**: A node's `HomeProvider` must be an ancestor in the tree |
213 | | -6. **Index Requirement**: All node navigations (folder/toc/docset) must have an Index |
214 | | -7. **Path Prefix Uniqueness**: In assembler builds, all `path_prefix` values must be unique |
215 | | - |
216 | | -## Performance Characteristics |
217 | | - |
218 | | -- **Tree Construction**: O(n) where n = number of files |
219 | | -- **URL Calculation**: O(depth) for first access, O(1) with caching |
220 | | -- **Re-homing**: O(1) - just replace HomeProvider reference |
221 | | -- **Tree Traversal**: O(n) for full tree, but rarely needed |
222 | | -- **Memory**: O(n) for nodes, URLs computed on-demand |
| 41 | +**For specific topics:** |
| 42 | +- How assembly works: [Assembler Process](assembler-process.md) |
| 43 | +- How re-homing works: [Home Provider Architecture](home-provider-architecture.md) |
| 44 | +- Two-phase approach: [Two-Phase Loading](two-phase-loading.md) |
| 45 | +- Node reference: [Node Types](node-types.md) |
0 commit comments