Cesivi Server - Architecture & Design Decisions¶
Home → Documentation → Reference → Architecture
This document explains the architectural design, key decisions, and patterns used in the Cesivi Server.
System Overview¶
The mock server implements SharePoint Server Subscription Edition using a 4-layer architecture:
Layer 4: Endpoints
REST API → SOAP Services → CSOM → PnP PowerShell
↓
Layer 3: Initialization & Business Logic
MockData Seeding → CSOM Processor → Services
↓
Layer 2: Persistence (IStorageService)
FileSystemStorageService | ADMockStorageService
↓
Layer 1: Domain Models
SPWeb, SPList, SPListItem, SPField, SPFile, etc.
Key Architectural Decision: REST Wraps SOAP¶
Principle: All endpoints delegate to a single source of truth
All REST endpoints ultimately call the same SOAP services: - REST → SharePointApiController → _listsService.GetListsAsync() - SOAP → ListsController → _listsService.GetListsAsync() - CSOM → CsomProcessor → _listsService.GetListsAsync()
Benefits: - ✅ 100% consistency between REST and SOAP - ✅ Zero duplicate business logic - ✅ Changes propagate to all protocols - ✅ Easier testing (test service once, use everywhere)
Layer 1: Domain Models (Type-Safe Design)¶
Philosophy: Strongly-Typed, No Dictionaries¶
// ✅ Good: Type-safe, compile-time safety
public class SPList
{
public string Title { get; set; }
public List<SPListItem> Items { get; set; }
public List<SPField> Fields { get; set; }
}
// ❌ Avoided: Runtime-unsafe
public class SPList
{
public Dictionary<string, object> Properties { get; set; }
}
Key Characteristics¶
- No Dictionary
- Compile-time safety - Nullable Reference Types -
string?prevents null exceptions - Enums for Constants -
ListBaseType.DocumentLibrary - JSON Property Ordering -
[JsonProperty(Order = X)]for CSOM - Comprehensive Properties - Match real SharePoint objects
Model Hierarchy¶
SPSecurable (permissions)
├─ SPWeb (site)
├─ SPList (list/library)
├─ SPListItem (item)
├─ SPField (column)
└─ SPContentType (content type)
SPPrincipal (security)
├─ SPUser
├─ SPGroup
└─ SPRole
SPFile (documents)
└─ SPFolder
Layer 2: Persistence (IStorageService)¶
Philosophy: Async-First, Dependency Injection¶
public interface IStorageService
{
// All methods are async
Task<SPWeb> LoadWebAsync(string webId, CancellationToken ct = default);
Task SaveWebAsync(SPWeb web, CancellationToken ct = default);
Task<List<SPList>> LoadAllListsAsync(string webId, CancellationToken ct = default);
}
Implementations (7 Providers)¶
FileSystemStorageService: - Stores data as JSON on disk - ReaderWriterLockSlim for concurrent reads - Atomic writes with backup - SQLite sidecar index for query push-down - MoveFolderAsync / CopyFolderAsync with recursive operations
InMemoryStorageService: - ConcurrentDictionary-based in-memory storage - Full feature parity with FileSystem - Ideal for testing and ephemeral scenarios
Database Providers (SQLite, LiteDB, SQL Server, PostgreSQL, MySQL): - Full SQL/LINQ query push-down (WHERE, ORDER BY, LIMIT, OFFSET) - Transactional consistency - Configurable via IStorageService DI
ADMockStorageService: - Users/groups in Active Directory mock - LDAP-compatible responses
Key Principles¶
- No Blocking Calls - All I/O is async/await
- Concurrent Reads - ReaderWriterLockSlim optimization
- Atomic Writes - Entire object or nothing
- Automatic Backup - Recovery if corruption
- Testable - Interface-based, swappable implementations
File System Structure¶
@MockData/
Farm/
settings.json
GlobalTermStore/settings.json
SearchIndex/index.json
Services/
WebApplications/{webapp}/
settings.json
SiteCollections/{sc}/
settings.json
Users/{userId}.json
Groups/{groupId}.json
TermStore/settings.json
RecycleBin/{itemId}.json
RootWeb/
settings.json
ContentTypes/{ctId}.json
Fields/{fieldId}.json
Roles/{roleId}.json
Lists/{listName}/
settings.json
Fields/{fieldId}.json
Views/{viewId}.json
Items/{itemId}.json
Attachments/{itemId}/{filename}
Libraries/{libName}/
settings.json
{filename}
{filename}.meta.json
.versions/{filename}/{version}
Key Patterns¶
File Storage Pattern¶
- Binary files stored directly in filesystem
- Companion
.meta.jsonfor metadata - Versions:
.versions/{filename}/{versionLabel} - Attachments:
Lists/{list}/Attachments/{itemId}/
Permission Inheritance¶
- Hierarchical: File → Folder → List → Web → Site
HasUniqueRoleAssignmentsflag breaks inheritanceGetEffectivePermissions()walks parent chain
Search Indexing¶
- Inverted index: Term → [DocumentId, Position, Frequency]
- TF-IDF scoring for relevance ranking
- Incremental indexing on Add/Update/Delete operations
- Storage: @MockData/Farm/SearchIndex/index.json
Content Types¶
- Hierarchical inheritance: List CT → Web CT → Site CT → Built-in CT
- Stored at web level: ContentTypes/{ctId}.json
- List associations: reference or copy web CT
Data Model Examples¶
Web (SPWeb)¶
{
"Id": "guid",
"Title": "Team Site",
"Url": "http://server/sites/site",
"Description": "...",
"Created": "2025-01-01T00:00:00",
"HasUniqueRoleAssignments": false
}
List (SPList)¶
{
"Id": "guid",
"Title": "Documents",
"BaseType": 1,
"BaseTemplate": 101,
"ItemCount": 0,
"EnableVersioning": true,
"ContentTypesEnabled": true
}
File Metadata¶
{
"Name": "document.pdf",
"ServerRelativeUrl": "/Documents/document.pdf",
"TimeCreated": "2025-01-01T00:00:00",
"TimeLastModified": "2025-01-02T00:00:00",
"Length": 1024,
"CheckOutType": 0,
"CheckedOutByUserId": null,
"MajorVersion": 1,
"MinorVersion": 0
}
Critical Architectural Decisions¶
Decision 1: CSOM Coverage Achieved ~98%¶
History: Early investigations (S2.641) concluded CSOM coverage was limited to ~70.3% due to undocumented protocol. Subsequent development (MASTERPLAN v23-v37, 2026-02-07 through 2026-02-12) proved this wrong — systematic CSOM implementation achieved ~98% coverage (~508/518 tests) by reverse-engineering the protocol through real SharePoint VM captures. Current State: ~381+ operations across 15+ object models, ~98% test pass rate
Decision 2: 95%+ Coverage = Production Ready¶
Philosophy: "Acceptable Excellence"
Coverage < 80% = Unacceptable (incomplete)
Coverage 80-90% = Good (production-ready with workarounds) ← RestSoap (78.7%)
Coverage 90-95% = Very Good (minor edge cases only)
Coverage 95%+ = Excellent (production quality) ← Main (97.6%)
Why Not 100%? - Last 5% of functionality takes 50% of effort - Real-world usage doesn't require 100% - Better to have 80% working than 100% partially broken - Document gaps as known limitations - Focus on highest-ROI features instead
Decision 3: Async-First, No Blocking Calls¶
Pattern: All I/O is async/await
// ✅ Good: Async all the way
public async Task<SPWeb> GetWebAsync(string webId)
{
return await _storage.LoadWebAsync(webId);
}
// ❌ Avoided: Blocking calls
public SPWeb GetWeb(string webId)
{
return _storage.LoadWebAsync(webId).Result; // BLOCKS!
}
Benefits: - Better scalability (doesn't block thread pool) - Responsive (doesn't starve other requests) - ASP.NET Core convention - Enables cancellation tokens
Decision 4: Testing Variance Tolerance¶
3-Run Median Methodology:
| Test Type | Variance | Reason | Assessment |
|---|---|---|---|
| In-Process (Main) | ±0 | Direct server, no HTTP | Perfect |
| External HTTP (RestSoap) | ±3.5 | HTTP layer variance | Excellent |
| CSOM/PnP | ±5-15 | Protocol complexity | Acceptable |
Why Variance is Normal: - HTTP request timing varies - Async task scheduling differs - Test isolation creates minor state differences - Concurrent file I/O effects
Variance is NOT: - ❌ Random test failures - ❌ Sign of unstable infrastructure - ❌ Reason to revert working code - ❌ Indicating bugs in implementation
Layer 3: Business Logic & Initialization¶
CSOM Query Processing¶
Converts CSOM ClientRequest objects to SharePoint operations:
public class CsomProcessor
{
// Processes: Query, Load, SetProperty, Update, etc.
// Returns: CsomResponse in special binary JSON format
// Handles: Complex object graphs, collections, method invocations
}
SOAP Service Pattern¶
Follows SharePoint SOAP conventions:
// Example: Lists.asmx GetList
public async Task<XmlDocument> GetListAsync(string listName)
{
var list = await _storage.GetListByTitleAsync(listName); // Service layer
return XmlSerializationHelper.SerializeListAsXml(list);
}
MockData Initialization¶
Bootstraps server with 531MB of test data: - Default sites and webs - Sample lists with items - Test users and groups - Sample files and folders - Event receiver configurations
Layer 4: Endpoints (REST, SOAP, CSOM, PnP)¶
REST API¶
- Framework: ASP.NET Core MVC
- Format: JSON
- Patterns: OData ($filter, $select, $expand)
- Status Codes: 200, 404, 400, 500, etc.
SOAP Services¶
- Framework: XML serialization
- Services: 26 implemented
- Patterns: CAML queries, method invocation
- Authentication: NTLM, Basic
CSOM¶
- Protocol: XML request, JSON response
- Complexity: Highest (property ordering critical)
- Coverage: ~98% (~508/518 tests, 381+ operations)
- Requirement:
[JsonProperty(Order = X)]on all models
PnP PowerShell¶
- Abstraction: Highest level (wraps CSOM + REST)
- Framework: PnP.PowerShell 3.1.0 (net481)
- Coverage: ~88% (196/273 tests, 27F/50S)
- Limitation: 27 failures + 50 skipped blocked by client-side PnP 3.x / CSOM protocol issues
Key Patterns¶
Pattern 1: Service Layer Abstraction¶
Business logic in services, controllers delegate:
public class ListsController : ControllerBase
{
public async Task<IActionResult> GetLists()
{
return Ok(await _listsService.GetListsAsync());
}
}
Pattern 2: Consistent Error Handling¶
throw new SPException(SPErrorCode.ListNotFound, "List not found");
Pattern 3: Defensive Defaults¶
public List<SPListItem> Items { get; } = new(); // Not null
Pattern 4: Configuration Over Convention¶
All settings in appsettings.json
Performance Optimizations¶
- ReaderWriterLockSlim - Multiple concurrent reads, single writer
- Lazy Loading - Collections load on-demand
- Connection Pooling - Kestrel reuses TCP connections
- JSON Caching - Frequently-accessed objects cached
Security Architecture¶
Authentication Schemes¶
- NTLM (Windows)
- Basic (username/password)
- Bearer (JWT tokens)
- Forms (ASP.NET)
Authorization¶
- Role-based access control (RBAC)
- Object-level permissions
- Permission inheritance
- Unique role assignments
Input Validation¶
- Path traversal prevention
- XML/JSON parsing safety
- URL validation
- Sanitization of user input
Summary¶
The architecture balances: - Simplicity - 4 clear layers - Testability - Abstracted storage, DI - Maintainability - Single source of truth - Performance - Async, pooling, caching - Security - Multiple auth, RBAC - Pragmatism - Accept constraints, focus on ROI
Philosophy: Production-ready software > Theoretical perfection
See Also: - QUICK_START.md - Getting started - KNOWN_LIMITATIONS.md - Known constraints - API_REFERENCE.md - Endpoint documentation - DEPLOYMENT_GUIDE.md - Running in production