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
| Port | Role |
|---|---|
9080 | HTTP proxy (data plane) |
9443 | HTTPS proxy |
9180 | Admin API + built-in Admin UI (/ui) |
9091 | Prometheus metrics (in-network apisix:9091, not host-published) |
There is no separate dashboard container or control port — host
9090belongs to Prometheus, not APISIX. Routing with path params (:account_id) requires theradixtree_uri_with_parameterrouter (set inconfig.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 StartupEvent →
ApisixRegistryService.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 seeder — make 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:
| Service | Image | Host port(s) | Purpose |
|---|---|---|---|
| postgres | postgres:17-alpine | 5432 | Shared DB — Process persistence + backs Apicurio/Keycloak/OpenFGA |
| redis | redis:8-alpine | 6379 | Caching for Process APIs |
| redpanda | redpandadata/redpanda:v26.1.10 | 9092, 29092 | Kafka-compatible event broker |
| kafka-console | redpandadata/console:v2.8.15 | 8084 | Redpanda console UI |
| apicurio | apicurio/apicurio-registry:3.3 | 8081 | Schema registry (Avro on Kafka) |
| keycloak | quay.io/keycloak/keycloak:26.6 | 8180 | OIDC / identity (realm afreximbank) |
| openfga | openfga/openfga:v1.18 | 8082 (HTTP), 8083 (gRPC) | Fine-grained ReBAC authorization |
| vault | hashicorp/vault:1.19 | 8200 | Secrets (dev mode) |
| etcd | bitnamilegacy/etcd:3.6.4 | 2379 | APISIX config store |
| apisix | apache/apisix:3.16.0-debian | 9080, 9443, 9180 | API gateway (REST→gRPC) |
| prometheus | prom/prometheus:v3 | 9090 | Metrics scraping |
| grafana | grafana/grafana:12.2 | 3001 | Dashboards |
| tempo | grafana/tempo:2.10.7 | 4317 (OTLP gRPC), 4318 (OTLP HTTP) | Tracing backend |
Ports & the gRPC model
- Default gRPC bind port
9090(and9091for dual-server System services like CBS). In%devevery service gets unique ports — e.g.cbs-finacle-sapi: HTTP8501, gRPC9501(account) /9502(facility);payment-processing-papi: HTTP8601, gRPC9601. - 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 realmafreximbank).
Supporting infrastructure
| Service | Role |
|---|---|
| Keycloak | OIDC for external users; OAuth2 client-credentials for internal M2M. Realm afreximbank. |
| OpenFGA | Relationship-based (ReBAC) fine-grained authorization; model in infra/dev/openfga/model.fga. |
| Vault | Secrets via quarkus-vault 4.8.0; in K8s each service authenticates with its own ServiceAccount (Kubernetes auth role). |
| Apicurio | Schema registry for Avro event serialization (SQL storage on a named Postgres datasource). |
| PostgreSQL 17 / Redis 8 | Process-API persistence and caching. |
| Redpanda | Kafka API for async domain events (+ console UI). |
| Prometheus / Grafana / Tempo + OpenTelemetry | Metrics, dashboards, and distributed tracing (OTLP → Tempo :4317). |
Build & deploy
- Jib images → ECR (
378457291432.dkr.ecr.eu-west-1.amazonaws.com, overrideREPOSITORY_URI/IMG_TAG), basebellsoft/liberica-runtime-container:jre-21-slim-glibc. Local k8s builds registry-less<svc>:latestimages. - Makefile:
infra-up/infra-down/infra-destroy/infra-logs/infra-ps;init-routes;images-system|process|experience|all;dev-<service>(=quarkusDev); and the k8s chainmake k8s-local(k8s-up → k8s-images → k8s-load-images → k8s-deploy → k8s-status), plusk8s-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 becausecbs-finacle-sapirequires 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.
:::