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 exceptionInsufficientBalanceException: Insufficient fundsOrderValidationException: Order validation failuresMarketClosedException: 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:
@Transactionalfor 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:
- User login with credentials
- JWT token generation
- Token validation on requests
- Role-based authorization
- 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:
@Asyncmethod 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.