Decouple all cross-app service calls to HTTP endpoints
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 3m0s

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>
This commit is contained in:
giles
2026-02-25 03:01:38 +00:00
parent 5dafbdbda9
commit 3b707ec8a0
55 changed files with 1210 additions and 581 deletions

View File

@@ -5,7 +5,8 @@ from sqlalchemy import select
from sqlalchemy.sql import func
from models.calendars import CalendarEntry, CalendarEntryPost
from shared.services.registry import services
from shared.infrastructure.data_client import fetch_data
from shared.contracts.dtos import PostDTO, dto_from_dict
async def add_post_to_entry(
@@ -28,8 +29,8 @@ async def add_post_to_entry(
return False, "Calendar entry not found"
# Check if post exists
post = await services.blog.get_post_by_id(session, post_id)
if not post:
raw = await fetch_data("blog", "post-by-id", params={"id": post_id}, required=False)
if not raw:
return False, "Post not found"
# Check if association already exists
@@ -103,7 +104,10 @@ async def get_entry_posts(
post_ids = list(result.scalars().all())
if not post_ids:
return []
posts = await services.blog.get_posts_by_ids(session, post_ids)
raw_posts = await fetch_data("blog", "posts-by-ids",
params={"ids": ",".join(str(i) for i in post_ids)},
required=False) or []
posts = [dto_from_dict(PostDTO, p) for p in raw_posts]
return sorted(posts, key=lambda p: (p.title or ""))
@@ -118,4 +122,8 @@ async def search_posts(
If query is empty, returns all posts in published order.
Returns (post_dtos, total_count).
"""
return await services.blog.search_posts(session, query, page, per_page)
raw = await fetch_data("blog", "search-posts",
params={"query": query, "page": page, "per_page": per_page},
required=False) or {"posts": [], "total": 0}
posts = [dto_from_dict(PostDTO, p) for p in raw.get("posts", [])]
return posts, raw.get("total", 0)