Orpheus Systems Architecture

3. `IDataService` with Local and SharePoint implementations

3. IDataService with Local and SharePoint implementations

Date: 2026-10-22

Status

Accepted


Problem

The calendar web part must work fully in the local workbench with bundled fixture data and against real SharePoint lists in production. Both demo roles must be showable without a tenant. Data access is PnPjs v4 (@pnp/sp 4.22). The field and form customizers and the application customizer also need models and role derivation.


Decision

We will define one interface in shared-core:

interface IDataService {
  getMyContracts(): Promise<Contract[]>;            // role-filtered
  getEntries(contractId, from, to): Promise<CalendarEntry[]>;
  upsertEntry(entry): Promise<CalendarEntry>;
  deleteEntry(contractId, date): Promise<void>;
  getHolidays(country, from, to): Promise<Holiday[]>;
  getCurrentUser(): Promise<UserInfo>;
}

with two implementations:

The service is chosen once in the web part's onInit and injected into the React tree via context. No component talks to SharePoint directly.

IDataService, its two implementations and its consumers

flowchart TB
  WP["Calendar web part<br/>onInit picks one service,<br/>React context injects it"]
  MCP["MCP server<br/>ADR 0011"]
  IDS[["IDataService<br/>shared-core<br/>getMyContracts, getCurrentUser<br/>getEntries, getHolidays<br/>upsertEntry, deleteEntry"]]
  LDS["LocalDataService"]
  SPDS["SharePointDataService"]
  FIX[("Bundled JSON fixtures<br/>in-memory writes")]
  LISTS[("Three lists chosen<br/>in the property pane<br/>used-days kept current")]
  WP -->|"components never<br/>call SharePoint directly"| IDS
  MCP -.->|"same interface"| IDS
  IDS -->|"Local environment<br/>or useLocalData on"| LDS
  IDS -->|"otherwise"| SPDS
  LDS --> FIX
  SPDS -->|"PnPjs v4<br/>CAML/OData filters<br/>batched writes"| LISTS

Considered Options

Option Summary Pros Cons
✅ Interface + Local + SharePoint implementations One seam, two back ends Workbench demo without tenant; testable logic; one place for role filtering and invariants Fixtures must be maintained alongside the schema
PnPjs calls inside components No abstraction Less code No workbench demo; untestable; invariants scattered
Mock PnPjs at the HTTP layer in the workbench Intercept requests Reuses the SharePoint path Fragile; harder to express demo roles

Consequences

Positive:

Negative / Trade-offs:

Risks: | Risk ID | Description | Impact (E/H/M/L) | Mitigation | |---------|-------------|------------------|------------| | R1 | Local and SharePoint implementations diverge | M | Shared unit tests run against both | | R2 | Used-days columns drift from entries | M | Recalculate on every write; roadmap option to recompute on read |