Skip to main content

ADR-0012: Dynamic API Prefixing and Service Namespacing

  • Status: Implemented
  • Date: 2026-05-11

Context

As Framework M moves towards a macroservice architecture, micro-frontends (MFEs) need to communicate with their respective backend services without hardcoding full URLs or being statically bound to a specific deployment path.

In a multi-service deployment, the backend might be structured as:

  • /api/finance/v1/...
  • /api/inventory/v1/...
  • /api/hr/v1/...

However, during local development or in a monolithic deployment, these might all live under a single /api/v1 prefix.

Previously, the frontend Desk and plugins assumed a flat /api/v1 structure. This created "cliffs" when moving from a monolith to a distributed macroservice deployment, as MFEs would try to hit the wrong endpoints.

Decision

Implement Dynamic API Prefixing and Service Namespacing across the entire stack.

1. Build-Time Service Injection

The @framework-m/vite-plugin now injects the FRAMEWORK_M_SERVICE_NAME environment variable into the frontend build via Vite's define configuration. This allows an MFE to know which "service" it belongs to at build/runtime.

2. Runtime URL Construction

The frontend constants (API_URL, META_URL, WS_URL) in framework-m-desk now dynamically construct endpoints:

  • If a service name is present, they use /api/{service}/v1.
  • If an absolute base URL is provided (via __FRAMEWORK_CONFIG__), they normalize it by ensuring the correct versioned/service-prefixed path is appended if missing.
  • They prioritize explicit configuration overrides (e.g., wsBaseUrl) while maintaining robust fallbacks.

3. Namespaced Resource Resolution

The frameworkMDataProvider (and the underlying buildResourceUrl helper) now supports namespaced resource strings (e.g., finance/PurchaseInvoice).

  • When a namespaced resource is requested, the provider splits it into service and resource.
  • It reconstructs the URL to point to the correct service-scoped endpoint (e.g., http://base.com/api/finance/v1/PurchaseInvoice).
  • This allows a central "Shell" to communicate with multiple federated macroservices seamlessly.

4. Backend Prefixing (Macroservice Mode)

The Python WebAdapter in framework-m-standard is updated to automatically apply the /api/{service_name}/{version} prefix to all registered routers when the application is running in Macroservice mode.

5. Naming & Versioning Governance

  • Service Names: Must be lowercase (e.g., finance, inventory).
  • Resource Namespacing: Resources across services use the service/DocType pattern.
  • Environment Parity: The injected FRAMEWORK_M_SERVICE_NAME must exactly match the namespace used in cross-service resource resolution.
  • Version Control: The api_version (e.g., v1, v2) is a Framework-level configuration concern. It is managed via FRAMEWORK_API_VERSION (backend) and window.__FRAMEWORK_CONFIG__.apiVersion (frontend). Individual app developers MUST NOT hardcode these versions in their code or routing logic.

Consequences

Positive

  • Architectural Isolation: MFEs are no longer bound to a flat global namespace.
  • Zero-Cliff Migration: The same MFE code works in both Monolith (/api/v1) and Macroservice (/api/{service}/v1) modes without changes.
  • Improved Proxying: Clearer path-based routing for API gateways and reverse proxies.

Negative

  • Complexity: URL construction logic is more complex than simple concatenation.
  • Dependency: MFEs must be built with the correct FRAMEWORK_M_SERVICE_NAME environment variable if they require namespacing.

References