website refactor

This commit is contained in:
2026-01-12 01:45:19 +01:00
parent fefd8d1cd6
commit 61db9116f6
11 changed files with 375 additions and 3 deletions

View File

@@ -0,0 +1,16 @@
/**
* ApiClient contract
*
* Transport boundary for API calls.
* Returns API Transport DTOs only.
*
* Based on WEBSITE_CONTRACT.md:
* - API Transport DTOs are returned by the backend API over HTTP
* - Website uses ApiClient to communicate with the API
*/
export interface ApiClient<TApiDto = unknown> {
/**
* Execute an API call and return the transport DTO
*/
execute(...args: unknown[]): Promise<TApiDto>;
}

View File

@@ -0,0 +1,29 @@
/**
* DisplayObject contract
*
* Deterministic, reusable, UI-only formatting/mapping logic.
*
* Based on DISPLAY_OBJECTS.md:
* - Class-based
* - Immutable
* - Deterministic
* - Side-effect free
* - No Intl.* or toLocale*
* - No business rules
*/
export interface DisplayObject {
/**
* Format or map the display object
*
* @returns Primitive values only (strings, numbers, booleans)
*/
format(): unknown;
/**
* Optional: Get multiple display variants
*
* Allows a single DisplayObject to expose multiple presentation formats
*/
variants?(): Record<string, unknown>;
}

View File

@@ -1,4 +1,5 @@
import type { PageQueryResult } from '@/lib/page-queries/PageQueryResult';
import { PageQueryResult } from "@/lib/page-queries/page-query-result/PageQueryResult";
/**
* PageQuery contract interface

View File

@@ -0,0 +1,21 @@
/**
* Presenter contract
*
* Pure, deterministic transformation between presentation models.
*
* Based on PRESENTERS.md:
* - Deterministic
* - Side-effect free
* - No HTTP/API calls
* - Maps between Page DTO, ViewModel, and ViewData
*/
export interface Presenter<TInput, TOutput> {
/**
* Transform input to output
*
* @param input - The input presentation model (Page DTO, ViewModel, etc.)
* @returns The output presentation model (ViewModel, ViewData, etc.)
*/
present(input: TInput): TOutput;
}

View File

@@ -0,0 +1,37 @@
/**
* Service contract
*
* Orchestration boundary for server-side operations.
* Returns API DTOs or Page DTOs only.
* Must be stateless.
*
* Based on WEBSITE_CONTRACT.md:
* - Services orchestrate IO and composition
* - They do not prepare UI
* - They return ApiDto or PageDto only
*/
/**
* Base service interface for orchestration operations
*/
export interface Service<TApiDto = unknown, TPageDto = unknown> {
/**
* Execute a service operation
* Returns either API Transport DTO or Page DTO
*/
execute(...args: unknown[]): Promise<TApiDto | TPageDto>;
}
/**
* Service that returns API Transport DTOs
*/
export interface ApiService<TApiDto = unknown> extends Service<TApiDto, never> {
execute(...args: unknown[]): Promise<TApiDto>;
}
/**
* Service that returns Page DTOs
*/
export interface PageService<TPageDto = unknown> extends Service<never, TPageDto> {
execute(...args: unknown[]): Promise<TPageDto>;
}

View File

@@ -0,0 +1,44 @@
/**
* Primitive types for contracts
*
* Reusable type definitions that can be used across all contracts.
*/
/**
* Represents any JSON-serializable value
*/
export type JsonValue =
| string
| number
| boolean
| null
| JsonValue[]
| { [key: string]: JsonValue };
/**
* Represents a JSON-serializable object (not array)
*/
export type JsonObject = {
[key: string]: JsonValue;
};
/**
* Represents a JSON-serializable array
*/
export type JsonArray = JsonValue[];
/**
* Helper type to ensure a type is JSON-serializable
*
* Usage:
* ```typescript
* type MyData = JsonSerializable<{
* title: string;
* count: number;
* items: string[];
* }>;
* ```
*/
export type JsonSerializable<T> = {
[K in keyof T]: T[K] extends JsonValue ? T[K] : never;
};

View File

@@ -0,0 +1,38 @@
/**
* ViewData contract
*
* Represents the shape of data that can be passed to Templates.
*
* Based on VIEW_DATA.md:
* - JSON-serializable only
* - Contains only template-ready values (strings/numbers/booleans)
* - MUST NOT contain class instances
*
* This is a type-level contract, not a class-based one.
*/
import type { JsonValue, JsonObject } from '../types/primitives';
/**
* Base interface for ViewData objects
*
* All ViewData must be JSON-serializable.
* This type ensures no class instances or functions are included.
*/
export interface ViewData extends JsonObject {
[key: string]: JsonValue;
}
/**
* Helper type to ensure a type is ViewData-compatible
*
* Usage:
* ```typescript
* type MyViewData = ViewData & {
* title: string;
* count: number;
* items: string[];
* };
* ```
*/
export type ViewDataOf<T extends ViewData> = T;

View File

@@ -0,0 +1,36 @@
/**
* ViewModelBase contract
*
* Base class for all ViewModels.
*
* Based on VIEW_MODELS.md:
* - Client-only classes
* - Fully prepared UI state
* - Never serialized
* - Never passed to Templates
* - Compose Display Objects for reusable formatting
* - Expose UI-specific computed properties
*
* Based on WEBSITE_CONTRACT.md:
* - ViewModels are client-only
* - Must not expose methods that return Page DTO or API DTO
*
* Architecture Flow:
* 1. PageQuery returns Page DTO (server)
* 2. Presenter transforms Page DTO → ViewModel (client)
* 3. Presenter transforms ViewModel → ViewData (client)
* 4. Template receives ViewData only
*
* ViewModels provide UI state and helpers.
* Presenters handle the transformation to ViewData.
*/
export abstract class ViewModel {
/**
* Optional: Validate the ViewModel state
*
* Can be used to ensure the ViewModel is in a valid state
* before a Presenter converts it to ViewData.
*/
validate?(): boolean;
}