Architecture
FluxiQ PIX is designed as a high-throughput, fault-tolerant platform for processing PIX instant payments. The architecture leverages Elixir/OTP's concurrency model to achieve the target throughput of 5,000 sustained TPS with 7,500 TPS burst capacity.
System Overview
┌─────────────────────────────────┐
│ BACEN (Central Bank) │
│ ┌──────────┐ ┌───────────┐ │
│ │ DICT API │ │ SPI API │ │
│ └────▲─────┘ └─────▲─────┘ │
└───────┼────────────────┼────────┘
mTLS mTLS
┌───────┼────────────────┼────────┐
│ │ FluxiQ PIX │ │
│ ┌────▼─────┐ ┌─────▼─────┐ │
│ │ DICT │ │ SPI │ │
│ │ Service │ │ Service │ │
│ │ :4001 │ │ :4002 │ │
│ └────┬─────┘ └─────┬─────┘ │
│ │ NATS │ │
│ └───────┬────────┘ │
│ ┌────▼─────┐ │
│ │Settlement│ │
│ │ Service │ │
│ │ :4003 │ │
│ └────┬─────┘ │
│ │ │
│ ┌────────────┼──────────────┐ │
│ │ PostgreSQL │ Redis │ NATS │ │
│ └────────────┴──────┴──────┘ │
└─────────────────────────────────┘Umbrella Application Structure
The backend is organized as an Elixir umbrella project. Each application in the umbrella has a well-defined responsibility and communicates with others through the shared library and NATS messaging.
Shared (apps/shared)
The shared application provides common functionality used across all services:
- Ecto Schemas - Database models for keys, transactions, settlements, participants
- BACEN HTTP Client - mTLS-enabled HTTP client for DICT and SPI APIs
- Authentication - JWT token generation and validation, session management
- Redis Client - Connection pooling and caching abstractions
- NATS Client - Pub/sub messaging for inter-service communication
- Telemetry - Metrics collection and distributed tracing
# Example: Shared BACEN client usage
Shared.Bacen.Client.post("/api/v2/key", payload,
cert: config[:cert_path],
key: config[:key_path],
cacerts: config[:ca_path]
)DICT Service (apps/dict_service - Port 4001)
Handles all PIX key directory operations in compliance with BACEN DICT API v2.10.0:
- Key registration and removal (CPF, CNPJ, PHONE, EMAIL, EVP)
- Key lookup by key value or by ISPB
- Claim creation and confirmation (portability and ownership)
- CID (Compulsory IDentifier) management
- Infraction reporting and MED 2.0 fraud prevention
SPI Service (apps/spi_service - Port 4002)
Processes real-time instant payments through the BACEN SPI infrastructure:
- Payment initiation (pacs.008) and confirmation (pacs.002)
- Payment return/reversal (pacs.004)
- QR Code generation and validation (static and dynamic)
- Real-time balance updates and position management
- Webhook notifications for payment status changes
Settlement Service (apps/settlement_service - Port 4003)
Manages settlement cycles, netting, and serves as the API gateway:
- API gateway with authentication, rate limiting, and request routing
- Multilateral netting calculations per settlement window
- STR (Sistema de Transferencia de Reservas) integration
- Reconciliation between internal records and BACEN statements
- Liquidity monitoring and alerts
Data Flow
Payment Initiation Flow
1. User Portal sends payment request
│
▼
2. Settlement Service (API Gateway) validates and authenticates
│
▼
3. DICT Service resolves the destination PIX key
│
▼
4. SPI Service constructs pacs.008 XML message
│
▼
5. SPI Service signs and sends to BACEN SPI via mTLS
│
▼
6. BACEN processes and returns pacs.002 confirmation
│
▼
7. SPI Service updates transaction status via NATS
│
▼
8. Settlement Service records for netting cycle
│
▼
9. User Portal receives real-time notification via WebSocketKey Registration Flow
1. Admin Portal submits key registration request
│
▼
2. DICT Service validates key format and ownership
│
▼
3. DICT Service sends CreateKey to BACEN DICT API
│
▼
4. BACEN validates uniqueness and returns confirmation
│
▼
5. DICT Service persists key and publishes event via NATS
│
▼
6. Admin Portal receives confirmationConcurrency Model
The platform exploits Elixir/OTP patterns for high throughput:
GenServer Pools
Each service maintains pools of GenServer workers for concurrent request processing:
# Connection pool for BACEN API calls
defmodule Shared.Bacen.Pool do
use Supervisor
def start_link(opts) do
Supervisor.start_link(__MODULE__, opts, name: __MODULE__)
end
def init(_opts) do
children = [
:poolboy.child_spec(:bacen_pool, pool_config(), [])
]
Supervisor.init(children, strategy: :one_for_one)
end
defp pool_config do
[
name: {:local, :bacen_pool},
worker_module: Shared.Bacen.Worker,
size: 50,
max_overflow: 25
]
end
endBroadway for Message Processing
NATS messages are processed using Broadway for batching and back-pressure:
defmodule SpiService.PaymentPipeline do
use Broadway
def start_link(_opts) do
Broadway.start_link(__MODULE__,
name: __MODULE__,
producer: [
module: {BroadwayNats, connection: :nats, subject: "pix.payments.>"}
],
processors: [
default: [concurrency: 100]
],
batchers: [
persistence: [concurrency: 10, batch_size: 50, batch_timeout: 100]
]
)
end
endETS for Hot Data
Frequently accessed data such as participant ISPB mappings and rate limits are stored in ETS tables for sub-microsecond lookups:
defmodule Shared.Cache.Participants do
use GenServer
def lookup(ispb) do
case :ets.lookup(:participants, ispb) do
[{^ispb, data}] -> {:ok, data}
[] -> {:error, :not_found}
end
end
endDatabase Design
PostgreSQL is the primary data store, partitioned for write performance:
- pix_keys - Partitioned by key type, indexed on key value and ISPB
- transactions - Partitioned by date (monthly), indexed on end-to-end ID
- settlements - Partitioned by settlement date
- audit_log - Append-only log for compliance, partitioned by month
Redis is used as a complementary store for:
- Session tokens and refresh tokens
- Rate limiting counters (sliding window)
- Idempotency keys with TTL
- Real-time position/balance caches
Fault Tolerance
The OTP supervision tree ensures automatic recovery from failures:
Application
├── Shared.Supervisor
│ ├── Shared.Repo (Ecto)
│ ├── Shared.Redis.Pool
│ ├── Shared.Nats.Connection
│ └── Shared.Cache.Supervisor
├── DictService.Supervisor
│ ├── DictService.Endpoint (Phoenix)
│ ├── DictService.ClaimProcessor
│ └── DictService.SyncWorker
├── SpiService.Supervisor
│ ├── SpiService.Endpoint (Phoenix)
│ ├── SpiService.PaymentPipeline (Broadway)
│ └── SpiService.PositionManager
└── SettlementService.Supervisor
├── SettlementService.Endpoint (Phoenix)
├── SettlementService.NettingEngine
└── SettlementService.ReconciliationWorkerEach supervisor uses the :one_for_one strategy, so a crashed child process is restarted independently without affecting siblings.
Performance Targets
| Metric | Target | Mechanism |
|---|---|---|
| Sustained TPS | 5,000 | Connection pooling, Broadway batching |
| Burst TPS | 7,500 | ETS caching, overflow pools |
| P99 latency | < 200ms | Async processing, NATS pub/sub |
| Availability | 99.99% | OTP supervision, multi-node clustering |
| Recovery time | < 5s | Automatic supervisor restarts |