Skip to content

feat(app): service dependency graph with fail-fast and topological startup#17

Merged
BlindMaster24 merged 4 commits into
devin/1777022445-dockerfrom
devin/1777022676-service-deps
Apr 27, 2026
Merged

feat(app): service dependency graph with fail-fast and topological startup#17
BlindMaster24 merged 4 commits into
devin/1777022445-dockerfrom
devin/1777022676-service-deps

Conversation

@devin-ai-integration
Copy link
Copy Markdown

@devin-ai-integration devin-ai-integration Bot commented Apr 24, 2026

Summary

Declarative service dependency graph for src/app.ts. Every service now declares required/optional deps in one place; the app validates them at construction (fail-fast with a clear message) and boots services in topological order — both locally and inside Docker.

Why: previously config.services was accepted as-is, so disabling http while api was on would pass constructor, then crash deep in Api.run() with Service 'http' not registered. With the graph declared explicitly, any config.services combination is safe: invalid combos fail at boot with the exact list of missing edges; valid combos start dependencies before dependents automatically.

What's new:

  • src/services/registry.ts:
    • serviceDependencies: Record<AppServiceName, { required, optional }> — single source of truth for the graph. Current edges:
      • api, alice, vkApphttp (required)
      • viberbot, http (required)
      • tg, vkbot (required); image, timetable (optional)
      • google_calendarhttp, bot (required)
      • timetableparser (optional); imagehttp (optional)
      • http, parser, bot — no required deps
    • validateServiceDependencies(enabled) — throws ServiceDependencyError listing every missing edge. Example:
      Service dependency check failed:
        - 'api' requires 'http' to be enabled
        - 'tg' requires 'bot' to be enabled
      Add the missing services to config.services or remove the dependents.
      
    • topologicalSort(enabled) — Kahn's algorithm with input-order-stable tie-breaking. Throws ServiceCycleError if a cycle is ever introduced.
  • src/app.ts: constructor calls validateServiceDependencies + topologicalSort before registering services, so new App(['api']) fails immediately instead of crashing mid-boot.

Behavior changes (all positive, no API break):

  • config.services: ['api'] → now fails at boot with "'api' requires 'http'" instead of a stack trace inside api/run().
  • Parser/http always boot before timetable/api respectively, regardless of the order in config.services.
  • Optional deps keep existing soft-wire behavior (e.g. timetable without parser still runs self-heal, just skips flushCache).

Tests (tests/services/registry.test.ts, 15 new cases):

  • validateServiceDependencies: empty list, all deps present, missing required dep throws, multi-missing reports every edge, optional missing ignored.
  • topologicalSort: empty input, required dep ordering, optional dep ordering (parser → timetable), realistic production set, stable under input permutations.
  • Registry sanity: every service declared once, no self-dependencies, no dep declared both required and optional.

Verification:

  • pnpm run test:all — 30 files / 202 tests passed
  • pnpm run ts-check — clean
  • pnpm run lint — clean
  • pnpm run format:check — clean
  • pnpm audit --audit-level=low — no known vulnerabilities

Stacked on PR #16 (Docker).

Review & Testing Checklist for Human

  • Try config.services = ['api'] in config.ts and run pnpm start — expect ServiceDependencyError with "'api' requires 'http' to be enabled" at boot (no stack trace from inside api/run()).
  • Confirm full valid config (['http', 'parser', 'timetable', 'bot', 'tg', 'image', 'api']) still starts cleanly; log line "Загруженные сервисы: ..." shows services in dependency order.
  • Run a minimal "API-only" profile — config.services = ['http', 'api', 'timetable'] + docker compose up — expect healthy with no bot code running.

Notes

  • Follow-up PR J will expose the enabled-services graph via /api/health so operators can curl to see what's running.
  • Follow-up PR L (graceful shutdown) will drain in reverse topological order (dependents stop before dependencies).

Link to Devin session: https://app.devin.ai/sessions/7732f5fd16e9448295cbabeb8b5f471a
Requested by: @BlindMaster24


Open in Devin Review

@devin-ai-integration
Copy link
Copy Markdown
Author

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment and CI monitoring

devin-ai-integration[bot]

This comment was marked as resolved.

@devin-ai-integration devin-ai-integration Bot force-pushed the devin/1777022676-service-deps branch from d3f59a0 to 62a43bf Compare April 24, 2026 09:46
devin-ai-integration[bot]

This comment was marked as resolved.

@BlindMaster24 BlindMaster24 merged commit 4ddfff1 into devin/1777022445-docker Apr 27, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant