Appearance
Lessons Learnt
Experience notes from building HVC. These are informal observations — useful context before touching the code.
Multi-tenancy is the source of most complexity
- Subdomain → realm → tenant resolution touches every service and every query. Changes to tenant resolution ripple widely; keep it in one place (
ITenantService/MultiTenantConnectionInterceptor). - Deleting a tenant requires cleaning many tables (chat included) — that's why the admin API has a dedicated
TenantDeletionService.
Realtime has many moving parts
- SignalR (hvc-api) and WebSockets (hvc-ws) both push state; make sure clients subscribe to the right one and reconnect cleanly.
- WebRTC signaling over SignalR works but couples call flow to hub lifecycle — test reconnects and token refreshes carefully.
Recording pipeline is fragile by nature
- Files flow disk → blob (
hvc-filePushApi) → tenant storage (hvc-services) whilehvc-sinkapimarks events complete. Filename conventions (env__eventId__tenantId__...) are load-bearing — don't change them without updating all three services.
Environment-awareness in workers
hvc-servicesandhvc-sinkapispin up one worker per configured environment. Adding an environment means config in multiple places (processors[],environments[], connection strings).
Docs drift
- Several services (
hvc-wsSeq wiring,hvc-filePushApiconsole logging) lag their config. When touching a service, check its Serilog/Seq setup.
Shared libraries drift
hvc-admin-apiandhvc-filePushApieach carry their own copy of the blob wrapper instead of referencinghvc-storage. Prefer the shared library for new code.
See also
- Design Decisions — the "why" behind these systems.