Skip to main content

Infrastructure & APISIX

The platform's gateway and supporting services. The full local stack is a 13-service Docker Compose file (infra/dev/compose.yml), fronted by Apache APISIX, which transcodes external REST/JSON into the gRPC the services speak.

APISIX gateway

APISIX (apache/apisix:3.16.0-debian) is the single external entry point. It stores its config in etcd and uses the grpc-transcode plugin to turn REST/JSON requests into gRPC calls to the upstream service.

Ports

PortRole
9080HTTP proxy (data plane)
9443HTTPS proxy
9180Admin API + built-in Admin UI (/ui)
9091Prometheus metrics (in-network apisix:9091, not host-published)

There is no separate dashboard container or control port — host 9090 belongs to Prometheus, not APISIX. Routing with path params (:account_id) requires the radixtree_uri_with_parameter router (set in config.yaml).

How a request is transcoded

A registered route carries a proto_id (an uploaded, flattened proto), the fully-qualified gRPC service (proto-package + service-name), the gRPC method, and a gRPC upstream (scheme: grpc, round-robin). For example:

GET /api/v1/system/cbs/accounts/:account_id
→ gRPC AccountService/GetAccountById (upstream host.docker.internal:9501 in %dev)

Route registration (self-service)

Each service registers its own routes at startup — there is no central route file. The model is configured under apisix.registry (mapped by ApisixRegistryConfig, @ConfigMapping(prefix="apisix.registry")):

apisix:
registry:
enabled: true
services:
- proto-id: proto-cbs-account
proto-file: afreximbank/system/cbs/v1/account.proto
proto-package: afreximbank.system.cbs.v1
service-name: AccountService
upstream-port: ${cbs.grpc.account-port} # 9501 in %dev
routes:
- id: route-cbs-account-getById
method: GET
path: /api/v1/system/cbs/accounts/:account_id
grpc-method: GetAccountById
description: Get Finacle account by ID (ACID, IBAN, or FORACID)

Flow: ApisixRegistryLifecycle observes the Quarkus StartupEventApisixRegistryService.registerAll() waits for the Admin API to be ready (max-retries/retry-delay-ms), then PUTs the flattened proto to /apisix/admin/protos/{id} and each route to /apisix/admin/routes/{id} (via ApisixAdminClient, OkHttp, X-API-KEY header). A registration failure logs a warning but does not block service startup.

ProtoFlattener — APISIX cannot resolve proto imports, so the registrar inlines the referenced message/enum types into a self-contained proto (special- casing google.protobuf.Timestamp), stripping syntax/package/import/option lines and rewriting fully-qualified type references to short names.

Fallback seedermake init-routes (infra/dev/apisix/init-routes.sh) reproduces the same logic in shell for when services aren't running: it discovers every service's %dev apisix.registry block, flattens the proto, and PUTs protos + routes to the Admin API. It skips services whose .proto doesn't exist yet, so it grows as contracts are authored. Requires yq, jq, curl.

Dev infrastructure stack (infra/dev/compose.yml)

make infra-up brings up all 13 services:

ServiceImageHost port(s)Purpose
postgrespostgres:17-alpine5432Shared DB — Process persistence + backs Apicurio/Keycloak/OpenFGA
redisredis:8-alpine6379Caching for Process APIs
redpandaredpandadata/redpanda:v26.1.109092, 29092Kafka-compatible event broker
kafka-consoleredpandadata/console:v2.8.158084Redpanda console UI
apicurioapicurio/apicurio-registry:3.38081Schema registry (Avro on Kafka)
keycloakquay.io/keycloak/keycloak:26.68180OIDC / identity (realm afreximbank)
openfgaopenfga/openfga:v1.188082 (HTTP), 8083 (gRPC)Fine-grained ReBAC authorization
vaulthashicorp/vault:1.198200Secrets (dev mode)
etcdbitnamilegacy/etcd:3.6.42379APISIX config store
apisixapache/apisix:3.16.0-debian9080, 9443, 9180API gateway (REST→gRPC)
prometheusprom/prometheus:v39090Metrics scraping
grafanagrafana/grafana:12.23001Dashboards
tempografana/tempo:2.10.74317 (OTLP gRPC), 4318 (OTLP HTTP)Tracing backend

Ports & the gRPC model

  • Default gRPC bind port 9090 (and 9091 for dual-server System services like CBS). In %dev every service gets unique ports — e.g. cbs-finacle-sapi: HTTP 8501, gRPC 9501 (account) / 9502 (facility); payment-processing-papi: HTTP 8601, gRPC 9601.
  • System APIs run gRPC servers; Process APIs run gRPC servers and are clients of System APIs; Experience APIs are REST-only (no gRPC server) and are clients of Process APIs.
  • Health: /q/health/live + /q/health/ready (SmallRye Health). Metrics: /q/metrics (Micrometer/Prometheus). OIDC: quarkus-oidc, auth-server URL injected via env (Keycloak realm afreximbank).

Supporting infrastructure

ServiceRole
KeycloakOIDC for external users; OAuth2 client-credentials for internal M2M. Realm afreximbank.
OpenFGARelationship-based (ReBAC) fine-grained authorization; model in infra/dev/openfga/model.fga.
VaultSecrets via quarkus-vault 4.8.0; in K8s each service authenticates with its own ServiceAccount (Kubernetes auth role).
ApicurioSchema registry for Avro event serialization (SQL storage on a named Postgres datasource).
PostgreSQL 17 / Redis 8Process-API persistence and caching.
RedpandaKafka API for async domain events (+ console UI).
Prometheus / Grafana / Tempo + OpenTelemetryMetrics, dashboards, and distributed tracing (OTLP → Tempo :4317).

Build & deploy

  • Jib images → ECR (378457291432.dkr.ecr.eu-west-1.amazonaws.com, override REPOSITORY_URI/IMG_TAG), base bellsoft/liberica-runtime-container:jre-21-slim-glibc. Local k8s builds registry-less <svc>:latest images.
  • Makefile: infra-up/infra-down/infra-destroy/infra-logs/infra-ps; init-routes; images-system|process|experience|all; dev-<service> (= quarkusDev); and the k8s chain make k8s-local (k8s-up → k8s-images → k8s-load-images → k8s-deploy → k8s-status), plus k8s-secrets, k8s-infra, k8s-routes, k8s-port-forward.
  • Helm umbrella infra/prod/helm/afxm-enterprise (v0.1.0) registers all 16 services via one ranged template (ServiceAccount + Deployment + Service each; Vault agent-inject annotations). Infra subcharts are commented out (deployed separately / managed services).
  • Local slice (values-local.yaml) deploys only the datasource-free 5 Process + 3 Experience APIs (OIDC + OTEL disabled); all 8 System APIs are disabled locally because cbs-finacle-sapi requires Oracle/Finacle and the others wrap external systems with no local stand-in.

:::note Versions Values above come from the actual config files. Where the repo's prose (CLAUDE.md) and the version catalog disagree, the catalog/compose file wins — e.g. Quarkus 3.36.2, APISIX 3.16.0, Apicurio 3.3, Keycloak 26.6, OpenFGA v1.18. :::