Vitra DDD Architecture
This document describes the bounded contexts, ubiquitous language, and layering rules that govern the Vitra codebase. Every Go package belongs to exactly one layer and obeys the dependency direction below.
Allowed import direction: adapters → application → domain.
vitra (root) Runtime facade — what consumers import.
domain/ Aggregates, value objects, denials, typed invocations. Zero deps.
internal/application/ Use cases, the invocation pipeline, the capability gateway, ports.
internal/inmemory/ In-memory port adapters (tests + single-process kernel).
cmd/vitra/ Developer CLI (delivery adapter).
example/ Runnable documentation — not a supported API.Infrastructure packages (platform/* WebView adapters, internal/packaging, updater, internal/provenance, …) may import application/domain only. Nothing in domain/ may import an infrastructure package.
Bounded Contexts (Phase 1)
| Context | Package | Core concepts |
|---|---|---|
| Identity & values | domain | AppID, WindowID, Origin, PermissionName, CommandName, GrantName |
| Window lifecycle | domain | Window aggregate, navigate/close |
| Capability | domain | CapabilityGrant, PathScope, Decision, DenialCode, EffectiveSurface |
| Commands | domain | CommandDefinition, CommandExecutor, Invocation |
| Authorization pipeline | internal/application | CapabilityGateway, InvocationService |
| Resources | internal/application | ResourceHandle ownership |
| Runtime orchestration | internal/application | open/navigate/close window, register grant/command, invoke, inspect |
| Delivery | vitra, cmd/vitra | facade + CLI |
Ubiquitous Language
- Caller — validated IPC sender identity (
Window+Origin) established at the native boundary, never from message payload fields alone. - Capability grant — explicit, inspectable authority binding windows, origins, and permissions (with optional path scope).
- Command — explicitly registered application operation; exported Go methods are not commands.
- Permission — named privileged native operation (e.g.
fs.read). - Origin — content origin of a WebView; packaged apps use
app://local. - Denial — deterministic, coded capability refusal (
ErrDenied+DenialCode). - Effective surface — inspectable projection of privileges for a window at its current origin.
Aggregates
| Aggregate | Invariants |
|---|---|
Window | Non-empty id/origin; navigation forbidden when closed; caller unavailable when closed |
CapabilityGrant | At least one window, origin, and permission; no duplicate permissions; deny paths win |
CommandDefinition | Non-empty name and required permission |
ResourceHandle (internal/application) | Owned by exactly one window; close is idempotent |
Services (internal/application)
The authorization pipeline is internal: apps reach it through vitra.Runtime (Invoke, Authorize, InspectCapabilities), so it can change without breaking the public domain API.
CapabilityGateway— evaluates grants; first allow wins; denials are specific and stable.InvocationService— identify → resolve command → authorize → execute.
Ports
internal/application:GrantRepository,WindowRepository,CommandRepository,ResourceRepository,SubscriptionRepository,CommandExecutorLookupdomain:CommandExecutor, the one port apps implement
Composition Root
vitra.New(Config) is the composition root. It wires in-memory adapters and use cases. Platform hosts (platform/linux|darwin|windows) construct the same use cases with platform-backed ports rather than inventing parallel invocation paths.
Testing
- Domain tests use in-package fakes or pure constructors (no
inmemoryimport required; some tests use local fakes). - Application tests drive use cases with
internal/inmemory/adapters. - Root
vitra_testcovers security invariants end-to-end through the facade. - Prefer table-driven tests; race-detector clean for concurrent repositories.
Security Mapping
Architectural tests live next to the code that enforces them. See docs/intent.md § Security Invariants and domain/pathscope_test.go, internal/application/gateway_test.go, internal/application/invocation_test.go, vitra_test.go.