Skip to main content

0059: Localhost Adapter Pattern

Date: 2025-12-22

Status: Accepted

Context​

The user-directory-service must support multiple upstream Identity Providers (IdPs) such as Keycloak, R-Auth, Auth0, and Azure AD. Each IdP has a unique API for managing users and invites.

Implementing the logic for all these IdPs within a single monolithic service would lead to:

  1. Dependency Bloat: The service would require client libraries and dependencies for every supported IdP.
  2. Tight Coupling: A bug or breaking change in one IdP integration could impact the entire service.
  3. Language Lock-in: We would be forced to write all integrations in the same language as the main service (Go), even if a better SDK exists in Python or Node.js.

We need an architecture that allows for modular, isolated, and potentially polyglot integrations.

Decision​

We will adopt the Localhost Adapter Pattern (also known as the Sidecar Pattern in Kubernetes).

  1. Main Service as Router: The core user-directory-service will act as a router and controller. It handles the public API, domain logic (Invites), and routing. It does not contain any IdP-specific logic.

  2. Adapters as Sidecars: Each IdP integration will be built as a standalone microservice (e.g., directory-adapter-keycloak, directory-adapter-rauth).

    • In Kubernetes, these will run as sidecar containers within the same Pod as the main service.
    • In Development, these will run as separate processes (or containers) on the same network.
  3. Communication via Localhost: The main service will communicate with the active adapters via HTTP over localhost.

    • The main service is configured with the URLs of its adapters via environment variables (e.g., ADAPTER_URL_KEYCLOAK_DEFAULT=http://localhost:9002).
    • This avoids the complexity and latency of routing traffic through an external load balancer or service mesh for internal calls.
  4. Standardized Internal API: All adapters must implement a strict, standardized internal API contract (defined in ADR-0058 and the codebase). This ensures the main service can treat all adapters uniformly.

Consequences​

Positive​

  • Decoupling: Adapters can be developed, tested, and deployed independently (though typically versioned together). A crash in an adapter does not crash the main service.
  • Polyglot Support: An adapter can be written in any language (e.g., Python for AI-based matching, Node.js for Firebase) as long as it exposes the standard HTTP interface.
  • Simplicity: The main service remains lean and focused on orchestration.

Negative​

  • Deployment Complexity: Kubernetes Pod specs become more complex, requiring multiple container definitions.
  • Resource Overhead: Running multiple processes/containers consumes more memory than a single monolithic binary.