Skip to content

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
elixir
# 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 WebSocket

Key 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 confirmation

Concurrency Model

The platform exploits Elixir/OTP patterns for high throughput:

GenServer Pools

Each service maintains pools of GenServer workers for concurrent request processing:

elixir
# 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
end

Broadway for Message Processing

NATS messages are processed using Broadway for batching and back-pressure:

elixir
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
end

ETS for Hot Data

Frequently accessed data such as participant ISPB mappings and rate limits are stored in ETS tables for sub-microsecond lookups:

elixir
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
end

Database 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.ReconciliationWorker

Each supervisor uses the :one_for_one strategy, so a crashed child process is restarted independently without affecting siblings.

Performance Targets

MetricTargetMechanism
Sustained TPS5,000Connection pooling, Broadway batching
Burst TPS7,500ETS caching, overflow pools
P99 latency< 200msAsync processing, NATS pub/sub
Availability99.99%OTP supervision, multi-node clustering
Recovery time< 5sAutomatic supervisor restarts

FluxiQ PIX - Plataforma Brasileira de Pagamento Instantaneo