Infrastructure as Code: Deploying a Financial Ecosystem with Docker Compose

2 11
calendar_todayschedule3 min read
— Originally published at dev.to

Hey folks!

In the previous post, I introduced the overview of My Broker B3. Today we'll talk about the foundation that supports the entire ecosystem: the infrastructure.

For a microservices system of this complexity, manually configuring each database, message broker and monitoring tool would be unmanageable. The solution was to use Docker Compose to create a reproducible local environment that faithfully replicates the needs of a distributed system.


️ The Infrastructure Map

The docker-compose.yml orchestrates 12 containers organized in 5 layers:

┌─────────────────────────────────────────────┐
│           RELATIONAL LAYER (SQL)            │
│  identity-db  wallet-db  order-db  asset-db │
│     MySQL        MySQL    MySQL     MySQL   │
│                  b3-core-db (PostgreSQL)    │
├─────────────────────────────────────────────┤
│              NOSQL LAYER                    │
│           broker-mongodb (Mongo 6.0)        │
├─────────────────────────────────────────────┤
│              CACHE LAYER                    │
│    broker-asset-cache    b3-market-cache    │
│         Redis Alpine          Redis Alpine  │
├─────────────────────────────────────────────┤
│             MESSAGING LAYER                 │
│    kafka (KRaft)        rabbitmq (AMQP)     │
├─────────────────────────────────────────────┤
│           OBSERVABILITY LAYER               │
│        prometheus            grafana        │
└─────────────────────────────────────────────┘

️ Design Decision: Domain Isolation

The most important infrastructure decision was data isolation by domain. Instead of a monolithic database, each microservice has its own instance:

# Each service has its own database — failures are isolated
broker-identity-db:
  image: mysql:8.0
  ports:
    - '3306:3306'  # identity

broker-wallet-db:
  image: mysql:8.0
  ports:
    - '3307:3306'  # wallet

broker-order-db:
  image: mysql:8.0
  ports:
    - '3308:3306'  # orders

broker-asset-db:
  image: mysql:8.0
  ports:
    - '3309:3306'  # assets

b3-core-db:
  image: postgres:15
  ports:
    - '5432:5432'  # B3 core

Why does this matter? If the orders database has a problem, the identity service keeps running. Failures stay isolated in their domain.


Cache: Isolated Instances by Context

Two Redis instances with distinct purposes:

broker-asset-cache:
  image: redis:7.0-alpine
  ports:
    - '${REDIS_BROKER_ASSET_PORT}:6379'  # prices for the broker

b3-market-cache:
  image: redis:7.0-alpine
  ports:
    - '${REDIS_B3_MARKET_PORT}:6379'    # prices for the matching engine

The b3-market-sync-api writes to b3-market-cache and the b3-matching-engine-api reads from it to decide whether to execute or reject an order. The separation ensures that broker-side problems don't affect the B3 side and vice versa.


Messaging: Two Brokers, Two Purposes

Apache Kafka — Internal Event Bus

Configured in modern KRaft mode (no Zookeeper), lighter and more stable for local development:

kafka:
  image: apache/kafka:3.7.0
  environment:
    KAFKA_PROCESS_ROLES: broker,controller
    KAFKA_LISTENERS: EXTERNAL://0.0.0.0:9092,INTERNAL://0.0.0.0:29092,CONTROLLER://0.0.0.0:9093
    KAFKA_ADVERTISED_LISTENERS: EXTERNAL://localhost:9092,INTERNAL://finance-kafka:29092
    KAFKA_CLUSTER_ID: MkU3OEVBNTcwNTJENDM2Qk

Used for the ecosystem's internal topics:

  • assets-market-data-v1 — asset quotes
  • order-events-v1 — order lifecycle events

RabbitMQ — Broker ↔ B3 Integration

rabbitmq:
  image: rabbitmq:3-management
  ports:
    - '5672:5672'    # AMQP
    - '15672:15672'  # Management UI

Used exclusively for communication between the broker and the B3 simulator:

  • mq-broker-to-b3 — orders sent to the matching engine
  • mq-b3-to-broker — execution feedback

Why two messaging systems? Kafka is ideal for event streams with replay and multiple consumers. RabbitMQ is ideal for work queues with point-to-point delivery guarantees. Each where it makes the most sense.


Observability from Day One

prometheus:
  image: prom/prometheus:latest
  volumes:
    - ./config/prometheus/prometheus.yml:/etc/prometheus/prometheus.yml
  ports:
    - '9090:9090'

grafana:
  image: grafana/grafana:latest
  ports:
    - '3000:3000'

All Spring Boot microservices export metrics via /actuator/health and /actuator/prometheus. Prometheus collects, Grafana visualizes. From the very first service that starts, we already have observability.


Security: No Hardcoded Secrets

All sensitive configuration lives in a .env file (ignored by Git):

# docker-compose.yml — references only
broker-identity-db:
  environment:
    MYSQL_DATABASE: ${BROKER_IDENTITY_DB_NAME}
    MYSQL_USER: ${BROKER_DB_USER}
    MYSQL_PASSWORD: ${BROKER_DB_PASS}
    MYSQL_ROOT_PASSWORD: ${BROKER_DB_ROOT_PASS}

A .env.example is committed as a template, but actual values never enter the repository.


How to Run

cp .env.example .env
# fill in the variables in .env

docker-compose up -d

A single command brings up all 12 containers. The development environment is identical for anyone who clones the repository.


What's Next?

With the infrastructure running, the next post shows the first microservice: broker-market-data-api — a Python service that fetches real quotes from Brapi, persists them in MongoDB and publishes to Kafka.


About the Series

⬅️ Previous Post: Building a Microservices Ecosystem

➡️ Next Post: Market Data Integrator: Consuming Real-Time Data with Python, MongoDB and Kafka

Series Index: Series Roadmap


Links:

481 Points13 Badges2 11
Caxias do Sul, Rio Grande do Sul, Brazillinkedin.com/in/roberto-de-vargas/?locale=en-US
9Posts
6Comments
6Followers
5Connections
Senior Software Engineer & Tech Lead driven by the challenge of designing resilient, scalable microservices architectures. I believe that good engineering is intentional—complexity... Show more
Build your own developer journey
Track progress. Share learning. Stay consistent.

3 Comments

1 vote
2
1
🔥 Join developers growing publicly
Share your knowledge, build in public, and grow your developer presence with a global community.

More Posts

The Audit Trail of Things: Using Hashgraph as a Digital Caliper for Provenance

Ken W. Algerverified - Apr 28

Local-First: The Browser as the Vault

Pocket Portfolio - Apr 20

Leveraging Laravel with Docker for Streamlined Development

MasterCraft - Mar 9

The Privacy Gap: Why sending financial ledgers to OpenAI is broken

Pocket Portfolio - Feb 23

From image to HTTPS endpoint in one step with ECS Express Mode

vishnu99 - Mar 19
chevron_left

Related Jobs

View all jobs →

Commenters (This Week)

1 comment
1 comment

Contribute meaningful comments to climb the leaderboard and earn badges!