Skip to main content

Backend Architecture - Java Spring Boot Microservices

Overview

The MyTradeX backend follows a modular microservices architecture built with Spring Boot. Each module is independently deployable and focuses on specific business domains, enabling scalability, maintainability, and clear separation of concerns.

Module Architecture

Backend Module Structure

mytradex-backend/
├── api/ # API layer and controllers
├── core/ # Entity definitions and DTOs
├── security/ # Authentication and security
├── service/ # Business logic services
├── repository/ # Data access layer
├── protocol/ # Exchange protocol handlers
└── pom.xml # Parent POM configuration

Core Modules

1. Core Module (mytradex-backend/core)

Purpose: Central entity definitions, DTOs, and business exceptions

Key Components:

Entity Hierarchy

  • BaseEntity: Common fields (id, createdAt, updatedAt)
  • User: User authentication and profile data
  • Order: Trading order lifecycle management
  • Trade: Executed trade records
  • Position: Portfolio positions and P&L
  • MarketData: Real-time market information
  • AuditLog: System activity tracking

Entity Relationships

@Entity
public class Order {
@Id
private UUID id;

@ManyToOne(fetch = FetchType.LAZY)
private User user;

@OneToMany(mappedBy = "order")
private List<Trade> trades;

@Enumerated(EnumType.STRING)
private OrderStatus status;

// Order details...
}

Business Exceptions

  • TradingException: Base trading business exception
  • InsufficientBalanceException: Insufficient funds
  • OrderValidationException: Order validation failures
  • MarketClosedException: Market hours validation

2. Security Module (mytradex-backend/security)

Purpose: Authentication, authorization, and security configuration

Security Configuration

@Configuration
@EnableWebSecurity
public class SecurityConfig {

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
return http
.csrf(AbstractHttpConfigurer::disable)
.authorizeHttpRequests(authz -> authz
.requestMatchers("/auth/**").permitAll()
.requestMatchers("/admin/**").hasRole("ADMIN")
.anyRequest().authenticated()
)
.sessionManagement(sess -> sess
.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.addFilterBefore(jwtAuthenticationFilter,
UsernamePasswordAuthenticationFilter.class)
.build();
}
}

JWT Service Implementation

@Service
public class JwtService {

public String generateToken(User user) {
Map<String, Object> claims = new HashMap<>();
claims.put("role", user.getRole().name());
claims.put("email", user.getEmail());
claims.put("name", user.getName());

return buildToken(claims, user.getId().toString());
}

public boolean isTokenValid(String token) {
try {
Jwts.parser()
.verifyWith(getSigningKey())
.build()
.parseSignedClaims(token);
return true;
} catch (JwtException | IllegalArgumentException e) {
return false;
}
}
}

User Details Service

  • Custom user details implementation
  • Role-based access control
  • Password encoding with BCrypt

3. Repository Module (mytradex-backend/repository)

Purpose: Data access layer with JPA repositories

Repository Patterns

1. Standard Repository Interface:

@Repository
@Transactional(readOnly = true)
public interface OrderRepository extends JpaRepository<Order, UUID> {

@EntityGraph(attributePaths = {"user"})
List<Order> findByUserId(UUID userId);

@EntityGraph(attributePaths = {"user"})
Optional<Order> findByIdAndUserId(UUID id, UUID userId);

@Query("SELECT o FROM Order o WHERE o.user.id = :userId AND o.status IN :statuses")
List<Order> findActiveOrders(@Param("userId") UUID userId,
@Param("statuses") Collection<Order.OrderStatus> statuses);
}

2. Entity Graph Usage:

  • Optimizes query performance with join fetching
  • Prevents N+1 query problems
  • Configurable fetch strategies

3. Custom Query Methods:

  • JPQL queries for complex business logic
  • Native SQL for performance-critical operations
  • Dynamic queries with Specifications

4. Service Module (mytradex-backend/service)

Purpose: Business logic implementation and orchestration

Core Services

OrderService: Order lifecycle management

@Service
@Transactional
public class OrderService {

public Order createOrder(User user, String symbol, Order.OrderSide side,
Order.OrderType type, BigDecimal quantity,
BigDecimal price, BigDecimal stopPrice,
Order.TimeInForce timeInForce) {

// Validate order
validateOrder(symbol, side, type, quantity, price, stopPrice);

Order order = Order.builder()
.user(user)
.symbol(symbol)
.side(side)
.type(type)
.quantity(quantity)
.price(price)
.status(Order.OrderStatus.NEW)
.remainingQuantity(quantity)
.build();

Order savedOrder = orderRepository.save(order);

// Emit WebSocket event
messagingTemplate.convertAndSend("/topic/orders/" + user.getId(), savedOrder);

return savedOrder;
}
}

TradeService: Trade execution and settlement

@Service
@Transactional
public class TradeService {

public Trade createTrade(UUID orderId, BigDecimal quantity, BigDecimal price,
User buyer, User seller, String protocolTradeId) {

Trade trade = Trade.builder()
.order(order)
.symbol(order.getSymbol())
.side(tradeSide)
.quantity(quantity)
.price(price)
.buyer(buyer)
.seller(seller)
.settlementStatus(Trade.SettlementStatus.PENDING)
.build();

Trade savedTrade = tradeRepository.save(trade);

// Update position
updatePosition(savedTrade);

return savedTrade;
}
}

PositionService: Portfolio and P&L management

@Service
@Transactional
public class PositionService {

public Position updatePosition(User user, String symbol,
BigDecimal quantity, BigDecimal price) {

Optional<Position> existingPosition =
positionRepository.findByUserIdAndSymbol(user.getId(), symbol);

Position position;
if (existingPosition.isPresent()) {
position = existingPosition.get();
position.addPosition(quantity, price);
} else {
position = Position.builder()
.user(user)
.symbol(symbol)
.quantity(quantity)
.averageCost(price)
.build();
}

return positionRepository.save(position);
}

@Transactional
public void recalculatePnLForSymbol(String symbol, BigDecimal marketPrice) {
List<Position> positions = positionRepository.findBySymbol(symbol);

for (Position position : positions) {
recalculatePnL(position, marketPrice);
}
}
}

MarketDataService: Real-time market data processing

@Service
public class MarketDataService {

@Transactional
public MarketData ingestITCHData(String symbol, BigDecimal lastPrice,
BigDecimal bidPrice, BigDecimal askPrice,
BigDecimal bidQuantity, BigDecimal askQuantity,
Long volume) {

Optional<MarketData> existingData = marketDataRepository.findBySymbol(symbol);

MarketData marketData;
if (existingData.isPresent()) {
marketData = existingData.get();
marketData.updatePrices(lastPrice, volume);
} else {
marketData = MarketData.builder()
.symbol(symbol)
.lastPrice(lastPrice)
.volume(volume)
.build();
}

MarketData savedData = marketDataRepository.save(marketData);

// Broadcast via WebSocket
broadcastMarketData(savedData);

// Recalculate P&L for positions
positionService.recalculatePnLForSymbol(symbol, lastPrice);

return savedData;
}
}

Service Layer Patterns

1. Transaction Management:

  • @Transactional for ACID compliance
  • Propagation strategies for complex operations
  • Rollback scenarios for failed trades

2. Event Publishing:

  • WebSocket event emission for real-time updates
  • Event-driven architecture for loose coupling
  • Message broadcasting to subscribed clients

3. Business Logic Validation:

  • Input validation at service layer
  • Business rule enforcement
  • Regulatory compliance checks

4. Error Handling:

  • Custom business exceptions
  • Proper error logging
  • Graceful degradation strategies

5. Protocol Module (mytradex-backend/protocol)

Purpose: Exchange protocol message processing and handling

Protocol Handlers

FIX Handler: Financial Information eXchange protocol

@Component
public class FixHandler {

public void processFixMessage(String fixMessage) {
log.info("FIX INCOMING: {}", fixMessage);

// Parse mock FIX message (simplified)
if (fixMessage.contains("35=D")) { // New Order Single
handleNewOrder(fixMessage);
} else if (fixMessage.contains("35=F")) { // Order Cancel Request
handleCancelOrder(fixMessage);
}
}

private void handleNewOrder(String fixMessage) {
// Parse FIX fields
String symbol = extractField(fixMessage, "55"); // Symbol
String side = extractField(fixMessage, "54"); // Side
String quantity = extractField(fixMessage, "38"); // Quantity
String price = extractField(fixMessage, "44"); // Price

log.info("FIX NEW ORDER: symbol={}, side={}, qty={}, price={}",
symbol, side, quantity, price);
}
}

ITCH Handler: Individual Trading and Clearing for Harmonized protocols

@Component
public class ItchHandler {

public void processItchMessage(byte[] message) {
log.info("ITCH INCOMING: {} bytes", message.length);

if (message.length > 0) {
char messageType = (char) message[0];

switch (messageType) {
case 'A': // Add Order
handleAddOrder(message);
break;
case 'E': // Order Executed
handleOrderExecuted(message);
break;
case 'P': // Trade
handleTrade(message);
break;
// Handle other message types...
}
}
}

private void handleAddOrder(byte[] message) {
ByteBuffer buffer = ByteBuffer.wrap(message).order(ByteOrder.BIG_ENDIAN);

char type = buffer.getChar();
long timestamp = buffer.getLong();
long orderRef = buffer.getLong();
char side = (char) buffer.get();
long quantity = buffer.getInt() & 0xFFFFFFFFL;
String symbol = new String(message, 24, 8).trim();

log.info("ITCH ADD ORDER: ref={}, side={}, qty={}, symbol={}",
orderRef, side, quantity, symbol);
}
}

OUCH Handler: OUCH protocol for order entry

@Component
public class OuchHandler {

public void processOuchMessage(byte[] message) {
// OUCH protocol message processing
// Similar structure to ITCH but for order entry
}
}

Protocol Features

1. Binary Message Parsing:

  • Efficient byte buffer handling
  • Big-endian network byte order
  • Structured message processing

2. Message Type Routing:

  • Switch-based message routing
  • Handler delegation patterns
  • Error handling for malformed messages

3. Mock Data Generation:

  • Scheduled mock message generation
  • Realistic market data simulation
  • Development and testing support

4. Protocol Health Monitoring:

  • Connection status tracking
  • Message processing metrics
  • Error rate monitoring

Cross-Cutting Concerns

1. Logging and Monitoring

Structured Logging:

  • SLF4J with Logback
  • JSON structured logs
  • Correlation ID tracking
  • Performance metrics

Health Checks:

  • Spring Boot Actuator
  • Database connectivity
  • External service availability
  • Custom health indicators

2. Database Management

Migration Strategy:

  • Liquibase for schema evolution
  • Versioned change sets
  • Rollback procedures
  • Data migration scripts

Connection Management:

  • HikariCP connection pooling
  • Transaction management
  • Query optimization
  • Database indexing

3. Security Implementation

Authentication Flow:

  1. User login with credentials
  2. JWT token generation
  3. Token validation on requests
  4. Role-based authorization
  5. Session management

Authorization Levels:

  • TRADER: Basic trading operations
  • ADMIN: User management and reporting
  • BROKER: Administrative overrides

4. Real-time Communication

WebSocket Integration:

  • Spring WebSocket support
  • STOMP message broker
  • Topic-based subscriptions
  • User-specific channels

Event Broadcasting:

  • Market data updates
  • Order status changes
  • Trade notifications
  • System alerts

Development Patterns

1. Dependency Injection

Constructor Injection:

@Service
@RequiredArgsConstructor
public class OrderService {

private final OrderRepository orderRepository;
private final SimpMessagingTemplate messagingTemplate;

// Business methods...
}

2. Configuration Management

External Configuration:

  • Application properties
  • Environment-specific configs
  • Secret management
  • Feature toggles

3. Error Handling

Global Exception Handler:

@RestControllerAdvice
public class GlobalExceptionHandler {

@ExceptionHandler(TradingException.class)
public ResponseEntity<ErrorResponse> handleTradingException(TradingException e) {
return ResponseEntity.badRequest()
.body(ErrorResponse.builder()
.code("TRADING_ERROR")
.message(e.getMessage())
.build());
}
}

4. Testing Strategy

Unit Testing:

  • Service layer testing
  • Repository testing with test containers
  • Mock-based testing
  • Performance testing

Integration Testing:

  • End-to-end API testing
  • Database integration tests
  • External service integration
  • Real-time communication testing

Performance Optimization

1. Database Optimization

Query Optimization:

  • Entity graph usage
  • Indexed columns
  • Query result caching
  • Batch operations

Connection Pooling:

  • HikariCP configuration
  • Connection validation
  • Idle connection cleanup
  • Maximum pool size tuning

2. Caching Strategy

Multi-level Caching:

  • Application-level caching
  • Database query caching
  • Market data caching
  • Session management

3. Asynchronous Processing

Async Operations:

  • @Async method execution
  • Event-driven processing
  • Background job processing
  • Non-blocking I/O

Next: Database Schema

Continue to Database Schema for detailed database design and data flow documentation.