facets
Typed profile extensions stored by Identity, owned by components
Facets enable components to extend Person/Group with typed preferences and settings. Each component defines facet schemas (e.g., TodoPreferencesFacet) while Identity handles storage. Facets are namespaced by component (todos.preferences, finance.settings) and support schema versioning for forward-compatible migrations. Changes emit events for sync.
1
Ports
2
Schemas
0
Hooks
2
Events
01
Ports
02
Schemas
Defines
Uses
No external schemas used
03
Hooks
No hooks declared
04
Events
- FacetUpserted v1
- FacetDeleted v1
05
Stories
06
Examples
from dataclasses import dataclass, field, asdict
from typing import Any
from psp.platform.facets import FacetPayload
@dataclass
class TodoPreferencesFacet(FacetPayload):
"""Per-person todo preferences."""
schema_version: int = 1
default_priority: str = "medium"
default_container_id: str | None = None
show_completed_tasks: bool = False
auto_archive_after_days: int | None = None
def to_dict(self) -> dict[str, Any]:
return asdict(self)
Create a dataclass implementing FacetPayload.
from psp.platform.facets import FacetKey, FacetReaderPort
class CreateTask:
def __init__(self, ..., facet_reader: FacetReaderPort):
self._facet_reader = facet_reader
def execute(self, input: CreateTaskInput, actor_id: UUID) -> Task:
# Load user preferences
key = FacetKey("person", actor_id, "todos.preferences")
prefs = self._facet_reader.get(key, TodoPreferencesFacet)
# Apply defaults from preferences
priority = input.priority
if priority is None and prefs:
priority = prefs.default_priority
priority = priority or "medium"
return Task(title=input.title, priority=priority, ...)
Inject FacetReaderPort and load user preferences.
from psp.platform.facets import FacetKey, FacetWriterPort
class UpdateTodoPreferences:
def __init__(self, facet_writer: FacetWriterPort):
self._facet_writer = facet_writer
def execute(self, actor_id: UUID, input: UpdatePreferencesInput) -> None:
key = FacetKey("person", actor_id, "todos.preferences")
prefs = TodoPreferencesFacet(
default_priority=input.default_priority,
show_completed_tasks=input.show_completed,
)
self._facet_writer.upsert(key, prefs)
Inject FacetWriterPort to update user preferences.
class TodosModule(Module):
FACETS = {
"todos.preferences": TodoPreferencesFacet,
"todos.settings": TodoSettingsFacet,
}
def register(self, registry: ComponentRegistry, ctx: RuntimeContext) -> None:
# Register facet schemas so Identity knows how to validate
for facet_key, facet_type in self.FACETS.items():
registry.add_facet_schema(facet_key, facet_type)
Components register facet schemas during startup.
API Reference
This component mounts routes under /v1/facets.
View OpenAPI specification