Files
mono/shared/services/registry.py
giles 3b707ec8a0 Decouple all cross-app service calls to HTTP endpoints
Replace every direct cross-app services.* call with HTTP-based
communication: call_action() for writes, fetch_data() for reads.
Each app now registers only its own domain service.

Infrastructure:
- shared/infrastructure/actions.py — POST client for /internal/actions/
- shared/infrastructure/data_client.py — GET client for /internal/data/
- shared/contracts/dtos.py — dto_to_dict/dto_from_dict serialization

Action endpoints (writes):
- events: 8 handlers (ticket adjust, claim/confirm, toggle, adopt)
- market: 2 handlers (create/soft-delete marketplace)
- cart: 1 handler (adopt cart for user)

Data endpoints (reads):
- blog: 4 (post-by-slug/id, posts-by-ids, search-posts)
- events: 10 (pending entries/tickets, entries/tickets for page/order,
  entry-ids, associated-entries, calendars, visible-entries-for-period)
- market: 1 (marketplaces-for-container)
- cart: 1 (cart-summary)

Service registration cleanup:
- blog→blog+federation, events→calendar+federation,
  market→market+federation, cart→cart only,
  federation→federation only, account→nothing
- Stubs reduced to minimal StubFederationService

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 03:01:38 +00:00

105 lines
3.2 KiB
Python

"""Typed singleton registry for domain services.
Each app registers ONLY its own domain service. Cross-app calls go
over HTTP via ``call_action()`` (writes) and ``fetch_data()`` (reads).
Usage::
from shared.services.registry import services
# Register at app startup (own domain only)
services.blog = SqlBlogService()
# Use locally within the owning app
post = await services.blog.get_post_by_slug(session, slug)
"""
from __future__ import annotations
from shared.contracts.protocols import (
BlogService,
CalendarService,
MarketService,
CartService,
FederationService,
)
class _ServiceRegistry:
"""Central registry holding one implementation per domain.
Properties return the registered implementation or raise
``RuntimeError`` if nothing is registered. Use ``has(name)``
to check before access when the domain might be absent.
"""
def __init__(self) -> None:
self._blog: BlogService | None = None
self._calendar: CalendarService | None = None
self._market: MarketService | None = None
self._cart: CartService | None = None
self._federation: FederationService | None = None
# -- blog -----------------------------------------------------------------
@property
def blog(self) -> BlogService:
if self._blog is None:
raise RuntimeError("BlogService not registered")
return self._blog
@blog.setter
def blog(self, impl: BlogService) -> None:
self._blog = impl
# -- calendar -------------------------------------------------------------
@property
def calendar(self) -> CalendarService:
if self._calendar is None:
raise RuntimeError("CalendarService not registered")
return self._calendar
@calendar.setter
def calendar(self, impl: CalendarService) -> None:
self._calendar = impl
# -- market ---------------------------------------------------------------
@property
def market(self) -> MarketService:
if self._market is None:
raise RuntimeError("MarketService not registered")
return self._market
@market.setter
def market(self, impl: MarketService) -> None:
self._market = impl
# -- cart -----------------------------------------------------------------
@property
def cart(self) -> CartService:
if self._cart is None:
raise RuntimeError("CartService not registered")
return self._cart
@cart.setter
def cart(self, impl: CartService) -> None:
self._cart = impl
# -- federation -----------------------------------------------------------
@property
def federation(self) -> FederationService:
if self._federation is None:
raise RuntimeError("FederationService not registered")
return self._federation
@federation.setter
def federation(self, impl: FederationService) -> None:
self._federation = impl
# -- introspection --------------------------------------------------------
def has(self, name: str) -> bool:
"""Check whether a domain service is registered."""
return getattr(self, f"_{name}", None) is not None
# Module-level singleton — import this everywhere.
services = _ServiceRegistry()