NV
NordVarg
ServicesTechnologiesIndustriesCase StudiesBlogAboutContact
Get Started

Footer

NV
NordVarg

Software Development & Consulting

GitHubLinkedInTwitter

Services

  • Product Development
  • Quantitative Finance
  • Financial Systems
  • ML & AI

Technologies

  • C++
  • Python
  • Rust
  • OCaml
  • TypeScript
  • React

Company

  • About
  • Case Studies
  • Blog
  • Contact

© 2025 NordVarg. All rights reserved.

October 28, 2024
•
NordVarg Team
•

Microservices vs Monoliths: What We Learned Building Trading Systems

Practical insights on when to use microservices and when a well-structured monolith is the better choice

ArchitectureArchitectureMicroservicesMonolithTrading Systems
7 min read
Share:

Introduction#

The microservices vs monolith debate often generates more heat than light. After building both architectures for high-stakes trading systems, we've learned that the answer is nuanced. This post shares our real-world experience.

The Microservices Promise#

Microservices offer compelling benefits:

  • Independent deployment - update services without full system restart
  • Technology diversity - use the best tool for each job
  • Team autonomy - teams own their services
  • Scalability - scale components independently

The Hidden Costs#

What the tutorials don't tell you:

1. Operational Complexity#

Instead of one system to monitor, you have dozens:

yaml
1# Suddenly you need all of this:
2services:
3  - order-service (3 instances)
4  - execution-service (2 instances)
5  - risk-service (4 instances)
6  - market-data-service (5 instances)
7  - position-service (2 instances)
8  - analytics-service (3 instances)
9  - notification-service (2 instances)
10
11monitoring:
12  - Prometheus (metrics)
13  - Grafana (dashboards)
14  - Jaeger (distributed tracing)
15  - ELK Stack (logging)
16  - PagerDuty (alerting)
17
18infrastructure:
19  - Kubernetes cluster
20  - Service mesh (Istio/Linkerd)
21  - Message broker (Kafka/RabbitMQ)
22  - API gateway
23  - Load balancers
24

Real cost: 3 DevOps engineers full-time vs 0.5 for a monolith.

2. Network Latency#

Every service boundary adds latency:

typescript
1// Monolith: 50μs
2function processOrder(order: Order): Result {
3  const risk = calculateRisk(order);      // function call
4  const position = getPosition(order);    // function call
5  return executeOrder(order, risk);       // function call
6}
7
8// Microservices: 5000μs
9async function processOrder(order: Order): Promise<Result> {
10  const risk = await riskService.calculate(order);      // HTTP: 1500μs
11  const position = await positionService.get(order);    // HTTP: 1500μs
12  return await executionService.execute(order, risk);   // HTTP: 2000μs
13}
14

100x latency increase - unacceptable for HFT systems.

3. Distributed Transactions#

A simple database transaction becomes a distributed saga:

typescript
1// Monolith: Simple transaction
2async function transferFunds(from: Account, to: Account, amount: number) {
3  await db.transaction(async (tx) => {
4    await tx.debit(from, amount);
5    await tx.credit(to, amount);
6  });
7}
8
9// Microservices: Saga pattern
10async function transferFunds(from: Account, to: Account, amount: number) {
11  const sagaId = generateId();
12  
13  try {
14    // Step 1: Debit
15    await accountService.debit(from, amount, sagaId);
16    
17    // Step 2: Credit
18    await accountService.credit(to, amount, sagaId);
19    
20    // Step 3: Confirm
21    await sagaService.complete(sagaId);
22  } catch (error) {
23    // Compensating transactions
24    await accountService.rollback(sagaId);
25    throw error;
26  }
27}
28

What could go wrong:

  • Network failure between steps
  • Partial completion
  • Compensation failures
  • Eventual consistency issues

4. Data Consistency#

Each service has its own database, leading to synchronization challenges:

typescript
1// Order Service DB
2{
3  orderId: "123",
4  symbol: "AAPL",
5  quantity: 100,
6  status: "FILLED"
7}
8
9// Position Service DB
10{
11  symbol: "AAPL",
12  quantity: 95  // Out of sync!
13}
14
15// Risk Service DB
16{
17  symbol: "AAPL",
18  exposure: 100 * 150  // Using stale quantity!
19}
20

When Microservices Make Sense#

Despite the challenges, microservices are the right choice when:

1. Truly Independent Domains#

Different business capabilities with minimal interaction:

plaintext
1┌─────────────────┐
2│  Trading        │
3│  Platform       │
4└─────────────────┘
5        ↓
6┌─────────────────┐     ┌─────────────────┐     ┌─────────────────┐
7│  Market Data    │     │  Reporting      │     │  Compliance     │
8│  (real-time)    │     │  (batch)        │     │  (async)        │
9└─────────────────┘     └─────────────────┘     └─────────────────┘
10
11These can be separate microservices - minimal interaction
12

2. Different Scaling Requirements#

plaintext
1Market Data Service:  [====] [====] [====] [====] [====]  (5 instances)
2Risk Service:         [====] [====] [====] [====]         (4 instances)
3Order Service:        [====] [====]                       (2 instances)
4Analytics:            [====]                              (1 instance)
5

3. Team Boundaries#

Large organization with independent teams:

plaintext
1Team A: Market Data (10 people, real-time specialists)
2Team B: Risk Engine (8 people, quantitative analysts)
3Team C: Order Management (6 people, trading domain experts)
4Team D: Analytics (5 people, data scientists)
5

When Monoliths Excel#

1. Low Latency Requirements#

For HFT systems, we use a modular monolith:

cpp
1// Single process, but modular
2class TradingEngine {
3private:
4  MarketDataModule market_data_;
5  RiskModule risk_;
6  OrderModule orders_;
7  ExecutionModule execution_;
8  
9public:
10  void on_market_data(const Quote& quote) {
11    // In-memory, zero-copy: 0.5μs
12    auto risk = risk_.check(quote);
13    
14    if (risk.allow) {
15      // Direct function call: 0.2μs
16      auto order = orders_.generate(quote, risk);
17      
18      // Inline execution: 0.3μs
19      execution_.send(order);
20    }
21    // Total: 1μs vs 5000μs for microservices
22  }
23};
24

2. Small to Medium Teams#

Team size < 20 people - coordination overhead of microservices exceeds benefits.

3. Rapid Development#

Startups and MVPs benefit from monolith simplicity:

typescript
1// Monolith: One repo, one deployment
2git push
3npm run build
4npm run deploy  // Done in 5 minutes
5
6// Microservices: Update 3 services
7cd order-service && git push && deploy
8cd risk-service && git push && deploy
9cd execution-service && git push && deploy
10// Configure service mesh, update routing, test integration
11// Done in 2 hours (if nothing breaks)
12

The Modular Monolith#

Our recommended approach for most financial systems:

typescript
1// Logical modules with clear boundaries
2src/
3  modules/
4    market-data/
5      domain/
6        models.ts
7        events.ts
8      application/
9        service.ts
10      infrastructure/
11        repository.ts
12    
13    risk/
14      domain/
15      application/
16      infrastructure/
17    
18    orders/
19      domain/
20      application/
21      infrastructure/
22
23// Enforced dependencies
24{
25  "rules": {
26    "market-data": ["risk"],          // market-data can depend on risk
27    "orders": ["risk", "market-data"], // orders depends on both
28    "risk": []                         // risk has no dependencies
29  }
30}
31

Benefits:#

✅ Low latency - function calls, not HTTP
✅ Simple deployment - single binary
✅ ACID transactions - real database transactions
✅ Easy debugging - single process to trace
✅ Future-proof - modules can be extracted to services later

Migration Strategy#

If you must migrate monolith → microservices:

Phase 1: Modularize#

typescript
1// Before: Spaghetti
2function processOrder(order) {
3  // 500 lines mixing everything
4}
5
6// After: Modules
7function processOrder(order) {
8  const risk = RiskModule.calculate(order);
9  const position = PositionModule.get(order);
10  return ExecutionModule.execute(order, risk);
11}
12

Phase 2: Extract Non-Critical Services#

Start with services that:

  • Have minimal dependencies
  • Are not latency-sensitive
  • Can tolerate eventual consistency
plaintext
1Monolith                    Microservices
2├── Market Data    ───────> Market Data Service (real-time)
3├── Risk                    
4├── Orders                  
5├── Execution               
6└── Analytics      ───────> Analytics Service (batch)
7└── Reporting      ───────> Reporting Service (batch)
8

Phase 3: Extract if Necessary#

Only extract core services if:

  • Clear team boundaries emerge
  • Independent scaling is essential
  • Technology diversity is required

Case Study: Our HFT Platform#

Initial architecture: Pure microservices (2019)

plaintext
115 microservices
25ms average latency
33 DevOps engineers
4$50k/month infrastructure
5Frequent outages from cascading failures
6

Current architecture: Modular monolith (2024)

plaintext
11 core trading monolith
23 separate services (analytics, reporting, compliance)
3200μs average latency (25x improvement)
41 DevOps engineer
5$8k/month infrastructure
6Zero outages in 18 months
7

Results:

  • 25x latency improvement
  • 84% infrastructure cost reduction
  • 67% reduction in DevOps overhead
  • 100% improvement in reliability

Conclusion#

Use microservices when:

  • You have truly independent business domains
  • Teams are large and geographically distributed
  • Different components need different technologies
  • You can afford the operational complexity

Use a modular monolith when:

  • Latency is critical (< 10ms requirements)
  • Team is < 20 people
  • You need rapid development
  • Business domains are tightly coupled

The truth: Most systems benefit from a well-structured monolith with optional microservices for genuinely independent capabilities.

Don't let architectural trends drive your decisions. Let your actual requirements (latency, team size, domain complexity) guide you.

Our Approach#

At NordVarg, we help clients:

  • Assess whether microservices are appropriate
  • Design modular monoliths with clean boundaries
  • Migrate from microservices to monoliths (yes, it happens!)
  • Extract microservices when truly necessary

Contact us to discuss your architecture challenges.

NT

NordVarg Team

Technical Writer

NordVarg Team is a software engineer at NordVarg specializing in high-performance financial systems and type-safe programming.

ArchitectureMicroservicesMonolithTrading Systems

Join 1,000+ Engineers

Get weekly insights on building high-performance financial systems, latest industry trends, and expert tips delivered straight to your inbox.

✓Weekly articles
✓Industry insights
✓No spam, ever

Related Posts

Sep 28, 2024•5 min read
Event Sourcing in Financial Systems: Patterns and Practices
How event sourcing provides auditability, temporal queries, and debugging superpowers in financial applications
ArchitectureEvent SourcingCQRS
Dec 30, 2024•6 min read
Building a Real-Time Risk Dashboard: From Data to Visualization
Architecturerisk-managementreal-time
Dec 29, 2024•5 min read
Time Synchronization in Distributed Trading Systems
Architecturetime-synchronizationptp

Interested in working together?