Implement durable store contract and registration roadmap

This commit is contained in:
2026-06-15 16:33:24 +02:00
parent 05596146c8
commit 2c94b40fc4
16 changed files with 1906 additions and 472 deletions

View File

@@ -7,20 +7,141 @@ adapters without changing domain code.
from __future__ import annotations
from contextlib import AbstractContextManager
from typing import Any, Iterable, Mapping, Protocol
from user_engine.domain import (
Account,
Actor,
Application,
ApplicationBinding,
AuditRecord,
AuthorizationDecision,
AuthorizationRequest,
CanonEntityReference,
Catalog,
ExternalIdentity,
FamilyInvitation,
Membership,
OutboxEvent,
ProfileValue,
TenantAccount,
User,
)
class UserEngineStore(Protocol):
"""Durable persistence boundary for user-engine service behavior.
Implementations may be in-memory, Postgres-backed, or platform-provided,
but must preserve the same logical keys, readiness contract, and atomic
mutation semantics exposed here.
"""
schema_version: str | None
@property
def ready(self) -> bool:
"""Return whether the store is schema-compatible for service use."""
def migrate(self) -> None:
"""Apply or verify user-engine-owned schema migrations."""
def transaction(self) -> AbstractContextManager[None]:
"""Return a context manager for one atomic mutation unit."""
def save_user(self, user: User) -> None:
"""Create or replace a user record."""
def user(self, user_id: str) -> User | None:
"""Return a user by id."""
def save_account(self, account: Account) -> None:
"""Create or replace a primary account record."""
def user_account(self, user_id: str) -> Account | None:
"""Return the primary account for a user."""
def save_identity(self, identity: ExternalIdentity) -> None:
"""Create or replace an external identity link."""
def find_identity(self, issuer: str, subject: str) -> ExternalIdentity | None:
"""Return an external identity by issuer and subject."""
def identities_for_user(self, user_id: str) -> tuple[ExternalIdentity, ...]:
"""Return all external identities linked to a user."""
def save_tenant_account(self, account: TenantAccount) -> None:
"""Create or replace a tenant-scoped account record."""
def tenant_account(self, tenant: str, user_id: str) -> TenantAccount | None:
"""Return a tenant-scoped account record."""
def save_membership(self, membership: Membership) -> None:
"""Create or replace a membership fact."""
def memberships_for_user(
self, user_id: str, *, tenant: str | None = None
) -> tuple[Membership, ...]:
"""Return memberships for a user, optionally scoped to a tenant."""
def memberships_for_tenant(self, tenant: str) -> tuple[Membership, ...]:
"""Return memberships scoped to a tenant."""
def save_application(self, application: Application) -> None:
"""Create or replace an application registration."""
def application(self, application_id: str) -> Application | None:
"""Return an application by id."""
def save_binding(self, binding: ApplicationBinding) -> None:
"""Create or replace an application binding."""
def binding(self, application_id: str) -> ApplicationBinding | None:
"""Return an application binding by application id."""
def save_catalog(self, catalog: Catalog) -> None:
"""Create or replace a catalog."""
def catalog(self, catalog_id: str) -> Catalog | None:
"""Return a catalog by id."""
def all_catalogs(self) -> tuple[Catalog, ...]:
"""Return all catalogs."""
def save_family_invitation(self, invitation: FamilyInvitation) -> None:
"""Create or replace a family invitation."""
def family_invitation(self, invitation_id: str) -> FamilyInvitation | None:
"""Return a family invitation by id."""
def family_invitations_for_user(
self, user_id: str
) -> tuple[FamilyInvitation, ...]:
"""Return family invitations for a user."""
def save_profile_value(self, value: ProfileValue) -> None:
"""Create or replace a profile value."""
def values_for_user(self, user_id: str) -> tuple[ProfileValue, ...]:
"""Return profile values for a user."""
def append_audit(self, record: AuditRecord) -> None:
"""Append a local audit record."""
def audit_log(self) -> tuple[AuditRecord, ...]:
"""Return local audit records in write order."""
def append_outbox(self, event: OutboxEvent) -> None:
"""Append an outbox event."""
def pending_outbox(self) -> tuple[OutboxEvent, ...]:
"""Return pending outbox events in write order."""
def record_counts(self) -> Mapping[str, int]:
"""Return adapter-neutral record counts for diagnostics."""
class IdentityClaimsAdapter(Protocol):
"""Normalize verified identity claims into a user-engine actor."""