Refactor: move telemetry track() calls from route handlers to application services
Opened by stack72 · 4/9/2026· GitHub #254
Context
From the DDD analysis of #253 (comment).
Problem
Telemetry track() calls live in route handlers — the HTTP adapter layer. This is consistent across the codebase (confirm.ts, [...path].ts, _middleware.ts, lib/auth.ts), but from a DDD standpoint the application service is the more principled location.
Route handlers are HTTP adapters. Application services represent use cases. "After successfully creating a collective, record that it happened" is part of the use case, not part of HTTP adaptation.
Concrete risks of the current pattern
- If a use case is ever invoked from a non-HTTP path (background job, CLI tool, service-to-service call), route-level telemetry gets silently skipped.
- Telemetry for the same domain operation is scattered across route files rather than co-located with the use case logic.
- Some telemetry requires pre-command lookups (e.g.,
findMemberByIdbefore member removal to capture data that won't exist after deletion). In app services, this data is often already available as part of the orchestration flow.
Current state
Today each app service is called from exactly one route, so the practical risk is zero. This is a code organization improvement, not a bug fix.
Proposal
Move track() calls from route handlers into application services (or have app services return enough context for a thin telemetry call at the boundary). This would:
- Co-locate telemetry with the use case it observes
- Eliminate pre-command lookups that exist solely for telemetry enrichment
- Ensure telemetry fires regardless of how the use case is invoked
Scope
lib/app/collective-commands.ts— all collective lifecycle events (#253)routes/api/v1/extensions/confirm.ts—extension_publishedroutes/api/v1/extensions/[...path].ts—extension_pulledlib/auth.ts— auth events (these are already in BetterAuth hooks, which is analogous to app-service-level placement)
Open question
Should app services call track() directly, or emit structured data that the caller dispatches? Direct calls are simpler; structured data keeps the app layer free of infrastructure imports. Could also be addressed by #255 (domain events).
Related
- #253 — Collective lifecycle telemetry (implements the route-level pattern)
- #255 — Domain events infrastructure
Open
No activity in this phase yet.
Sign in to post a ripple.