Skip to main content
← Back to list
01Issue
FeatureClosedSwamp Club
Assigneesstack72

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., findMemberById before 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:

  1. Co-locate telemetry with the use case it observes
  2. Eliminate pre-command lookups that exist solely for telemetry enrichment
  3. 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.tsextension_published
  • routes/api/v1/extensions/[...path].tsextension_pulled
  • lib/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).

  • #253 — Collective lifecycle telemetry (implements the route-level pattern)
  • #255 — Domain events infrastructure
02Bog Flow
OPENTRIAGEDIN PROGRESSCLOSEDTRIAGE+ 2 MOREREVIEW

Closed

4/10/2026, 1:07:33 PM

No activity in this phase yet.

03Sludge Pulse
stack72 assigned stack724/10/2026, 10:41:31 AM
Editable. Press Enter to edit.

stack72 commented 4/10/2026, 1:07:27 PM

Closing this as won't-fix. After triaging and planning the refactoring, the cost/benefit doesn't justify the work:

Costs:

  • Widening 8+ app service function signatures to thread telemetry context (userId, username, slug)
  • acceptInvitation/declineInvitation go from minimal deps (Pick<CollectiveCommandDeps, "auth">) to needing collectiveRepo + queries purely for telemetry enrichment
  • Pre-command lookups don't disappear — they just move from routes to app services
  • App services get less pure by coupling to telemetry infrastructure, not more
  • 3 test files break for zero user-visible behavior change
  • extension_pulled can't even be moved (no pull app service exists)

Benefits:

  • Theoretical: telemetry fires if a use case is invoked from a non-HTTP path
  • Practical: zero — each app service has exactly one caller today

The right solution here is #62 (domain events). That gives a principled, subscriber-based approach where telemetry observes domain operations without app services knowing about it. Manually moving track() calls now would be throwaway work once events land.

Sign in to post a ripple.