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.