Domain isolation: typed contracts, service registry, and composable wiring
Add typed service contracts (Protocols + frozen DTOs) in shared/contracts/ for cross-domain communication. Each domain exposes a service interface (BlogService, CalendarService, MarketService, CartService) backed by SQL implementations in shared/services/. A singleton registry with has() guards enables composable startup — apps register their own domain service and stubs for absent domains. Absorbs glue layer: navigation, relationships, event handlers (login, container, order) now live in shared/ with has()-guarded service calls. Factory gains domain_services_fn parameter for per-app service registration. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
111
services/stubs.py
Normal file
111
services/stubs.py
Normal file
@@ -0,0 +1,111 @@
|
||||
"""No-op stub services for absent domains.
|
||||
|
||||
When an app starts without a particular domain, it registers the stub
|
||||
so that ``services.X.method()`` returns empty/None rather than crashing.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
from decimal import Decimal
|
||||
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from shared.contracts.dtos import (
|
||||
PostDTO,
|
||||
CalendarDTO,
|
||||
CalendarEntryDTO,
|
||||
MarketPlaceDTO,
|
||||
ProductDTO,
|
||||
CartItemDTO,
|
||||
CartSummaryDTO,
|
||||
)
|
||||
|
||||
|
||||
class StubBlogService:
|
||||
async def get_post_by_slug(self, session: AsyncSession, slug: str) -> PostDTO | None:
|
||||
return None
|
||||
|
||||
async def get_post_by_id(self, session: AsyncSession, id: int) -> PostDTO | None:
|
||||
return None
|
||||
|
||||
async def get_posts_by_ids(self, session: AsyncSession, ids: list[int]) -> list[PostDTO]:
|
||||
return []
|
||||
|
||||
|
||||
class StubCalendarService:
|
||||
async def calendars_for_container(
|
||||
self, session: AsyncSession, container_type: str, container_id: int,
|
||||
) -> list[CalendarDTO]:
|
||||
return []
|
||||
|
||||
async def pending_entries(
|
||||
self, session: AsyncSession, *, user_id: int | None, session_id: str | None,
|
||||
) -> list[CalendarEntryDTO]:
|
||||
return []
|
||||
|
||||
async def entries_for_page(
|
||||
self, session: AsyncSession, page_id: int, *, user_id: int | None, session_id: str | None,
|
||||
) -> list[CalendarEntryDTO]:
|
||||
return []
|
||||
|
||||
async def entry_by_id(self, session: AsyncSession, entry_id: int) -> CalendarEntryDTO | None:
|
||||
return None
|
||||
|
||||
async def associated_entries(
|
||||
self, session: AsyncSession, content_type: str, content_id: int, page: int,
|
||||
) -> tuple[list[CalendarEntryDTO], bool]:
|
||||
return [], False
|
||||
|
||||
async def toggle_entry_post(
|
||||
self, session: AsyncSession, entry_id: int, content_type: str, content_id: int,
|
||||
) -> bool:
|
||||
return False
|
||||
|
||||
async def adopt_entries_for_user(
|
||||
self, session: AsyncSession, user_id: int, session_id: str,
|
||||
) -> None:
|
||||
pass
|
||||
|
||||
async def claim_entries_for_order(
|
||||
self, session: AsyncSession, order_id: int, user_id: int | None,
|
||||
session_id: str | None, page_post_id: int | None,
|
||||
) -> None:
|
||||
pass
|
||||
|
||||
async def confirm_entries_for_order(
|
||||
self, session: AsyncSession, order_id: int, user_id: int | None,
|
||||
session_id: str | None,
|
||||
) -> None:
|
||||
pass
|
||||
|
||||
async def get_entries_for_order(
|
||||
self, session: AsyncSession, order_id: int,
|
||||
) -> list[CalendarEntryDTO]:
|
||||
return []
|
||||
|
||||
|
||||
class StubMarketService:
|
||||
async def marketplaces_for_container(
|
||||
self, session: AsyncSession, container_type: str, container_id: int,
|
||||
) -> list[MarketPlaceDTO]:
|
||||
return []
|
||||
|
||||
async def product_by_id(self, session: AsyncSession, product_id: int) -> ProductDTO | None:
|
||||
return None
|
||||
|
||||
|
||||
class StubCartService:
|
||||
async def cart_summary(
|
||||
self, session: AsyncSession, *, user_id: int | None, session_id: str | None,
|
||||
page_slug: str | None = None,
|
||||
) -> CartSummaryDTO:
|
||||
return CartSummaryDTO()
|
||||
|
||||
async def cart_items(
|
||||
self, session: AsyncSession, *, user_id: int | None, session_id: str | None,
|
||||
) -> list[CartItemDTO]:
|
||||
return []
|
||||
|
||||
async def adopt_cart_for_user(
|
||||
self, session: AsyncSession, user_id: int, session_id: str,
|
||||
) -> None:
|
||||
pass
|
||||
Reference in New Issue
Block a user