4.3 KiB
Service Center vs Legacy Services Hub — Comparison & Assessment
Executive Summary
The Service Center (modules/serviceCenter) is a superior architecture compared to the legacy Services Hub (modules/services). It was worthwhile to create it. The main benefits are: explicit dependency graph, lazy loading, per-service RBAC, and context-scoped resolution without carrying the entire hub. The legacy hub remains valid for incremental migration and backward compatibility.
1. Architecture Comparison
| Aspect | Service Center | Legacy Services Hub |
|---|---|---|
| Location | modules/serviceCenter/ |
modules/services/ |
| Entry point | getService(key, context, legacy_hub) |
getInterface(user, ...) → Services |
| Constructor | (context, get_service) |
(services) — full hub |
| Dependencies | Declared in registry, resolved lazily via get_service("key") |
Via self.services.<attr> — all services always present |
| Loading | Lazy — only requested services + deps | Eager — everything at construction |
| RBAC | Per-service objectKey, can_access_service() |
Shared via hub .rbac |
| Caching | Per-context cache (user + mandate + featureInstance) | No instance cache — new Services each call |
| Feature override | N/A — features use getService directly |
Feature services override hub attributes |
| Pre-warm | preWarm() at app startup |
None |
| Structure | Core vs importable split; explicit registry | Flat serviceX/ dirs; discovery via glob |
2. Which Setup is Better?
Service Center is better for these reasons:
2.1 Explicit Dependency Graph
- Dependencies are declared in
registry.py(e.g."ai": {"dependencies": ["chat", "utils", "extraction", "billing"]}). - Circular dependencies are detected and raise
RuntimeError. - Easier to reason about and refactor.
2.2 Lazy Loading & Resource Efficiency
- Only requested services (and their transitive deps) are loaded.
- A feature like chatbot needs
chat,ai,billing,streaming— notsharepoint,ticket,neutralization, etc. - Legacy hub loads everything on first
getInterface().
2.3 Context-Scoped Resolution
- Each request gets a
ServiceCenterContext(user, mandate_id, feature_instance_id, workflow). - Resolution is cached per context. Same user+mandate+feature → same instances.
- No need to pass or construct a full hub.
2.4 Per-Service RBAC
- Services have
objectKey(e.g.service.ai,service.extraction). can_access_service(user, rbac, service_key)checks before resolving.- Finer-grained control than a single hub-level RBAC.
2.5 Separation of Concerns
- Core services (utils, security, streaming): internal, no RBAC.
- Importable services (ai, billing, extraction, etc.): feature-facing, RBAC-protected.
- Clear distinction vs. flat structure in legacy.
2.6 Pre-warm for Cold Start
preWarm()imports all service modules at startup.- First request avoids import latency.
- Legacy has no equivalent.
3. When Legacy Still Makes Sense
- Migration: Features that haven’t moved yet still use
getInterface(). - Feature overrides: Feature-specific services (e.g.
serviceAi/mainServiceAi.pyin a feature) that override hub attributes. - Backward compatibility:
legacy_hubfallback in Service Center allows gradual migration.
4. Did It Make Sense to Create the Service Center?
Yes. The legacy hub has inherent limitations:
- Monolithic hub — every
getInterface()constructs a fullServicesobject with all services, interfaces, and feature discovery. - Implicit dependencies — services grab what they need via
self.services.<attr>, leading to hidden coupling. - No explicit RBAC per service — access control is at the hub level.
- Eager loading — every request pays for all services even when only a few are used.
Service Center addresses these while keeping a migration path via legacy_hub fallback. The Chatbot feature already uses it successfully.
5. Benchmark Script
Run the comparison script to measure runtime and memory:
# From gateway root
python tests/benchmarks/benchmark_service_center_vs_legacy.py
See tests/benchmarks/benchmark_service_center_vs_legacy.py for details on metrics and methodology.