Skip to content

pacama95/suggestions_api

Repository files navigation

Portfolio Management API

A comprehensive reactive Quarkus-based REST API for portfolio management applications, featuring stock suggestions, market data integration, and financial instrument management. Built with Clean Architecture and Hexagonal Architecture patterns for enterprise-grade scalability and maintainability.

Architecture

This project follows Clean Architecture and Hexagonal Architecture principles with complete separation of concerns:

Domain Layer (domain/)

  • Models: Core business entities (Stock, Currency, Exchange, StockType, Errors)
  • Use Cases: Business logic interfaces for all operations (stocks, currencies, exchanges, stock types)
  • Ports: Interfaces for external dependencies (repositories, market data providers)

Application Layer (application/)

  • Services: Use case implementations with comprehensive business logic
  • Data Processing: Batch processing, validation, and error handling
  • External Integration: Market data fetching and storage orchestration

Infrastructure Layer (infrastructure/)

  • Web Adapters: REST API endpoints with comprehensive OpenAPI documentation
  • Repository Adapters: PostgreSQL database access with reactive Panache
  • External Clients: TwelveData API integration for real-time market data
  • Persistence: Liquibase database migrations and entity management
  • DTOs & Mappers: MapStruct-powered data transfer objects with @RegisterForReflection

Features

  • Reactive Programming: Built with Quarkus Reactive using Mutiny (Uni/Multi) for non-blocking operations
  • Clean Architecture: Domain-driven design with clear separation of concerns and SOLID principles
  • Comprehensive API: Multiple endpoints for stocks, currencies, exchanges, and stock types
  • Database Persistence: PostgreSQL with Hibernate Reactive Panache and Liquibase migrations
  • Market Data Integration: Real-time stock data from TwelveData API
  • Advanced Search: Sophisticated stock search with multiple criteria
  • Caching System: Caffeine-based caching for optimal performance
  • Batch Processing: Efficient bulk operations for large datasets
  • Admin Operations: Administrative endpoints for data management and imports
  • Native Compilation Ready: All DTOs annotated with @RegisterForReflection
  • Error Handling: Comprehensive validation and business error handling
  • OpenAPI Documentation: Complete API documentation with examples
  • Docker Ready: Docker Compose setup with PostgreSQL and Adminer
  • Test Coverage: Comprehensive unit and integration tests

Technology Stack

  • Java 21: Latest LTS version of Java
  • Quarkus 3.14.2: Reactive, Kubernetes-native Java framework
  • Mutiny: Reactive programming library for asynchronous data flows
  • PostgreSQL 15: Production-ready relational database
  • Hibernate Reactive: Non-blocking database access with Panache
  • Liquibase: Database schema versioning and migrations
  • TwelveData API: Real-time market data provider
  • Caffeine: High-performance in-memory caching
  • MapStruct: Type-safe bean mapping
  • Jakarta RESTful Web Services: For REST API endpoints
  • Jackson: JSON serialization/deserialization
  • SmallRye OpenAPI: API documentation generation
  • Hibernate Validator: Request validation
  • Docker & Docker Compose: Containerization and development environment
  • Gradle: Dependency management and build tool

Getting Started

Prerequisites

  • Java 21 or higher
  • Gradle 8.10 or higher (or use the included wrapper)
  • Docker & Docker Compose (for database setup)
  • TwelveData API Key (for market data integration)

Quick Start

  1. Start the database services:
docker-compose up -d
  1. Set up environment variables:
export TWELVE_DATA_API_KEY="your-api-key-here"
# Optional: customize database connection
export PGHOST=localhost
export PGPORT=5434
export PGUSER=postgres
export PGPASSWORD=suggestions_pass
export PGDATABASE=suggestions_db
  1. Run the application in development mode:
./gradlew quarkusDev

The application will start on http://localhost:8090 with live reload enabled.

Running Options

  1. Development Mode (with live reload):
./gradlew quarkusDev
  1. Production Mode:
./gradlew build
java -jar build/quarkus-app/quarkus-run.jar
  1. Native Executable (requires GraalVM):
./gradlew build -Dquarkus.package.type=native
./build/suggestions-api-1.0.0-runner

Access Points

Once the application is running, you can access:

API Endpoints

Stock Suggestions

Basic Stock Search

Search for stock ticker suggestions - optimized for frontend typeahead:

GET /suggestions?q={query}&limit={limit}

Parameters:

  • q (required): Search query (ticker symbol or company name)
  • limit (optional): Maximum results to return (1-50, default: 10)

Example:

curl "http://localhost:8090/suggestions?q=apple&limit=5"

Response:

{
  "suggestions": [
    {
      "symbol": "AAPL",
      "name": "Apple Inc.",
      "exchange": "NASDAQ",
      "type": "Common Stock",
      "country": "United States",
      "currency": "USD",
      "micCode": "XNAS",
      "figiCode": "BBG000B9XRY4"
    }
  ],
  "query": "apple",
  "count": 1
}

Advanced Stock Search

Advanced search with multiple criteria:

GET /v1/suggestions/advanced?symbol={symbol}&companyName={name}&exchange={exchange}&region={region}&currency={currency}&limit={limit}

Parameters:

  • symbol (optional): Stock symbol filter
  • companyName (optional): Company name filter
  • exchange (optional): Exchange code filter
  • region (optional): Region/country filter
  • currency (optional): Currency code filter
  • limit (optional): Maximum results (1-100, default: 20)

Example:

curl "http://localhost:8090/v1/suggestions/advanced?exchange=NYSE&currency=USD&limit=10"

Currencies

Get All Currencies

GET /v1/currencies?q={search}&limit={limit}

Parameters:

  • q (optional): Search query for currency code or name
  • limit (optional): Maximum results (1-100, default: 50)

Example Response:

{
  "currencies": [
    {
      "id": 1,
      "code": "USD",
      "name": "United States Dollar",
      "symbol": "$",
      "countryCode": "US",
      "active": true
    }
  ],
  "count": 1
}

Stock Exchanges

Get All Exchanges

GET /v1/exchanges?q={search}&limit={limit}

Parameters:

  • q (optional): Search query for exchange code or name
  • limit (optional): Maximum results (1-100, default: 50)

Example Response:

{
  "exchanges": [
    {
      "id": 1,
      "code": "NYSE",
      "name": "New York Stock Exchange",
      "country": "United States",
      "timezone": "America/New_York",
      "currencyCode": "USD",
      "active": true
    }
  ],
  "count": 1
}

Stock Types

Get All Stock Types

GET /v1/stock-types?q={search}&limit={limit}

Parameters:

  • q (optional): Search query for stock type code or name
  • limit (optional): Maximum results (1-100, default: 50)

Example Response:

{
  "stockTypes": [
    {
      "id": 1,
      "code": "CS",
      "name": "Common Stock",
      "description": "Ordinary shares that represent ownership in a company",
      "active": true
    }
  ],
  "count": 1
}

Admin Operations

Fetch and Store Stock Data

Administrative endpoint to populate the database with market data:

POST /admin/fetch-stocks

Description: Fetches all available stocks from TwelveData API and stores them in the database. This operation populates/refreshes the stock data used by the suggestions endpoints.

Response:

{
  "success": true,
  "message": "Successfully fetched and stored 5000 stocks from TwelveData API",
  "recordsProcessed": 5000
}

Note: This operation requires a valid TwelveData API key configured in the application.

Database Schema

The application uses PostgreSQL with Liquibase for schema management. The database contains the following main tables:

Core Tables

stocks

  • Purpose: Stores stock/ticker information from TwelveData API
  • Key Fields: symbol, name, currency, exchange, country, type, mic_code, figi_code, cfi_code, isin, cusip
  • Indexes: Optimized for symbol, name, exchange, and country searches
  • Features: Active status tracking, data versioning, audit timestamps

currencies

  • Purpose: Stores currency information (USD, EUR, JPY, etc.)
  • Key Fields: code, name, symbol, country_code, is_active
  • Indexes: Optimized for code, country, and active status searches

exchanges

  • Purpose: Stores stock exchange information (NYSE, NASDAQ, LSE, etc.)
  • Key Fields: code, name, country, timezone, currency_code, is_active
  • Indexes: Optimized for code, country, currency, and active status searches

stock_types

  • Purpose: Stores stock type categories (Common Stock, ETF, REIT, etc.)
  • Key Fields: code, name, description, is_active
  • Indexes: Optimized for code and active status searches

categories

  • Purpose: Additional categorization support
  • Note: Referenced in changelog, supporting extensible categorization

Database Migration

The database schema is managed through Liquibase with versioned changesets:

  • 001-create-stocks-table.yaml: Initial stock table structure
  • 002-create-categories-tables.yaml: Category and related tables

All migrations run automatically on application startup when quarkus.liquibase.migrate-at-start=true.

Docker Deployment

Development Environment

The project includes a complete Docker Compose setup for development:

Start Development Services

# Start PostgreSQL and Adminer
docker-compose up -d

# View logs
docker-compose logs -f

# Stop services
docker-compose down

Included Services

  • PostgreSQL 15: Main database on port 5434
  • Adminer: Database administration UI on port 8084

Database Access

  • Host: localhost:5434
  • Database: suggestions_db
  • User: postgres
  • Password: suggestions_pass
  • Adminer URL: http://localhost:8084

Application Deployment

Native Image (Recommended for Production)

# Build native image
docker build -f Dockerfile -t portfolio-api:native .

# Run with environment variables
docker run -i --rm -p 8090:8090 \
  -e TWELVE_DATA_API_KEY="your-api-key" \
  -e PGHOST="your-db-host" \
  -e PGPORT="5432" \
  -e PGUSER="postgres" \
  -e PGPASSWORD="your-password" \
  -e PGDATABASE="suggestions_db" \
  portfolio-api:native

JVM Mode (Faster builds for Development)

# Build the application
./gradlew build

# Build JVM image
docker build -f Dockerfile.jvm -t portfolio-api:jvm .

# Run JVM container
docker run -i --rm -p 8090:8090 \
  -e TWELVE_DATA_API_KEY="your-api-key" \
  portfolio-api:jvm

Production Docker Compose

For a complete production deployment, create a docker-compose.prod.yml:

version: '3.8'
services:
  portfolio-api:
    image: portfolio-api:native
    ports:
      - "8090:8090"
    environment:
      - TWELVE_DATA_API_KEY=${TWELVE_DATA_API_KEY}
      - PGHOST=postgres
      - PGPORT=5432
      - PGUSER=postgres
      - PGPASSWORD=${POSTGRES_PASSWORD}
      - PGDATABASE=suggestions_db
    depends_on:
      - postgres
      
  postgres:
    image: postgres:15-alpine
    environment:
      - POSTGRES_DB=suggestions_db
      - POSTGRES_USER=postgres
      - POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
    volumes:
      - postgres_data:/var/lib/postgresql/data
      
volumes:
  postgres_data:

Configuration

The application is configured through src/main/resources/application.properties with support for environment variable overrides:

Core Configuration

# Application
quarkus.application.name=suggestions-api
quarkus.application.version=1.0.0

# HTTP Server
quarkus.http.port=8090
quarkus.http.cors=true
quarkus.http.cors.origins=*

Database Configuration

# PostgreSQL Connection
quarkus.datasource.db-kind=postgresql
quarkus.datasource.username=${PGUSER:postgres}
quarkus.datasource.password=${PGPASSWORD:suggestions_pass}
quarkus.datasource.reactive.url=postgresql://${PGHOST:localhost}:${PGPORT:5434}/${PGDATABASE:suggestions_db}

# Connection Pool
quarkus.datasource.reactive.max-size=20
quarkus.datasource.reactive.idle-timeout=PT10M

# Hibernate ORM
quarkus.hibernate-orm.database.generation=validate
quarkus.hibernate-orm.log.sql=true
quarkus.hibernate-orm.log.format-sql=true

# Liquibase Migration
quarkus.liquibase.migrate-at-start=true
quarkus.liquibase.change-log=db/changelog.yaml
quarkus.liquibase.validate-on-migrate=true

External API Configuration

# TwelveData API
twelve.data.api.key=${TWELVE_DATA_API_KEY}
twelve.data.api.timeout.seconds=300

# REST Client
quarkus.rest-client.twelve-data-api.url=https://api.twelvedata.com
quarkus.rest-client.twelve-data-api.connect-timeout=30000
quarkus.rest-client.twelve-data-api.read-timeout=30000

Caching Configuration

The application uses Caffeine for high-performance caching:

# Stock Types Cache
quarkus.cache.caffeine.stock-types-all.expire-after-write=PT30M
quarkus.cache.caffeine.stock-types-search.expire-after-write=PT10M
quarkus.cache.caffeine.stock-types-by-code.expire-after-write=PT1H

# Currencies Cache  
quarkus.cache.caffeine.currencies-all.expire-after-write=PT30M
quarkus.cache.caffeine.currencies-search.expire-after-write=PT10M
quarkus.cache.caffeine.currencies-by-code.expire-after-write=PT1H

# Exchanges Cache
quarkus.cache.caffeine.exchanges-all.expire-after-write=PT30M
quarkus.cache.caffeine.exchanges-search.expire-after-write=PT10M
quarkus.cache.caffeine.exchanges-by-code.expire-after-write=PT1H

Environment Variables

Key environment variables for deployment:

  • TWELVE_DATA_API_KEY: Required for market data integration
  • PGHOST: Database host (default: localhost)
  • PGPORT: Database port (default: 5434)
  • PGUSER: Database user (default: postgres)
  • PGPASSWORD: Database password (default: suggestions_pass)
  • PGDATABASE: Database name (default: suggestions_db)

Testing

The project includes comprehensive test coverage with unit and integration tests:

Run All Tests

./gradlew test

Run with Coverage

./gradlew test jacocoTestReport

Test Reports

Test reports are generated in build/reports/tests/test/index.html

Test Architecture

  • Unit Tests: Domain logic, use cases, and services tested in isolation
  • Repository Tests: Database operations tested with test containers
  • Integration Tests: API endpoints tested end-to-end
  • Reactive Testing: Proper testing of Uni and Multi reactive streams

Performance & Monitoring

Caching Strategy

  • Caffeine Cache: In-memory caching for reference data (currencies, exchanges, stock types)
  • Cache Expiration: Intelligent expiration times based on data volatility
  • Cache Keys: Optimized for common query patterns

Database Optimization

  • Indexes: Strategic indexes on frequently queried fields
  • Reactive Queries: Non-blocking database operations
  • Connection Pooling: Optimized connection pool settings

API Performance

  • Reactive Architecture: Non-blocking request processing
  • Pagination: Efficient pagination for large datasets
  • Response Optimization: Minimal data transfer with focused DTOs

Deployment Considerations

For Frontend Integration

The API is optimized for modern frontend applications:

  • CORS Enabled: Cross-origin requests supported
  • RESTful Design: Standard HTTP methods and status codes
  • JSON API: Consistent JSON response format
  • OpenAPI Documentation: Complete API specification for code generation
  • TypeScript Support: Generate TypeScript clients from OpenAPI spec

Production Checklist

  • Configure production database with proper credentials
  • Set up TwelveData API key with appropriate rate limits
  • Enable HTTPS with proper SSL certificates
  • Configure monitoring and logging
  • Set up health checks and metrics
  • Implement rate limiting and API security
  • Configure backup and disaster recovery

Scalability

  • Reactive Architecture: High concurrency with low resource usage
  • Database Connection Pooling: Efficient database resource utilization
  • Caching Layer: Reduced database load for reference data
  • Stateless Design: Horizontal scaling capabilities

Project Structure

The project follows Clean Architecture with clear separation of layers:

src/main/java/com/portfolio/management/
├── domain/                                    # Domain Layer (Business Logic)
│   ├── model/                                # Domain Models
│   │   ├── Stock.java                        # Stock entity
│   │   ├── Currency.java                     # Currency entity  
│   │   ├── Exchange.java                     # Exchange entity
│   │   ├── StockType.java                    # Stock type entity
│   │   ├── StockProcessingResult.java        # Processing results
│   │   ├── StocksBatchProcessingResult.java  # Batch processing results
│   │   └── Errors.java                       # Error handling
│   └── port/                                 # Domain Ports (Interfaces)
│       ├── incoming/                         # Use Cases (Input Ports)
│       │   ├── GetSuggestionsUseCase.java            # Basic stock suggestions
│       │   ├── GetSuggestionsAdvancedUseCase.java    # Advanced stock search
│       │   ├── GetCurrenciesUseCase.java             # Currency operations
│       │   ├── GetCurrencyUseCase.java               # Single currency lookup
│       │   ├── GetExchangesUseCase.java              # Exchange operations
│       │   ├── GetExchangeUseCase.java               # Single exchange lookup
│       │   ├── GetStockTypesUseCase.java             # Stock type operations
│       │   ├── GetStockTypeUseCase.java              # Single stock type lookup
│       │   └── FetchAndStoreStockDataUseCase.java    # Data import operations
│       └── outgoing/                         # Output Ports (Dependencies)
│           ├── StockPort.java                        # Stock data access
│           ├── CurrencyRepository.java               # Currency data access
│           ├── ExchangeRepository.java               # Exchange data access
│           ├── StockTypeRepository.java              # Stock type data access
│           └── MarketDataPort.java                   # External market data
├── application/                              # Application Layer (Use Case Implementations)
│   └── service/                             # Application Services
│       ├── SuggestionService.java                   # Stock suggestions service
│       ├── AdvancedSuggestionService.java           # Advanced search service
│       ├── CurrencyService.java                     # Currency management
│       ├── ExchangeService.java                     # Exchange management  
│       ├── StockTypeService.java                    # Stock type management
│       └── FetchAndStoreStockDataService.java       # Data import service
├── infrastructure/                           # Infrastructure Layer (External Concerns)
│   └── adapters/
│       ├── incoming/                         # Input Adapters (Web)
│       │   └── web/                         # REST Controllers
│       │       ├── SuggestionsController.java       # Suggestions API interface
│       │       ├── SuggestionsResource.java         # Suggestions implementation
│       │       ├── CurrencyController.java          # Currency API interface
│       │       ├── CurrencyResource.java            # Currency implementation
│       │       ├── ExchangeController.java          # Exchange API interface
│       │       ├── ExchangeResource.java            # Exchange implementation
│       │       ├── StockTypeController.java         # Stock type API interface
│       │       ├── StockTypeResource.java           # Stock type implementation
│       │       ├── AdminController.java             # Admin API interface
│       │       ├── AdminResource.java               # Admin implementation
│       │       ├── dto/                            # Data Transfer Objects
│       │       │   ├── SuggestionsRequest.java     # Request DTOs
│       │       │   ├── SuggestionsResponse.java    # Response DTOs
│       │       │   ├── CurrencyResponse.java       # Currency DTOs
│       │       │   ├── ExchangeResponse.java       # Exchange DTOs
│       │       │   ├── StockTypeResponse.java      # Stock type DTOs
│       │       │   └── ErrorResponse.java          # Error DTOs
│       │       └── mapper/                         # MapStruct Mappers
│       │           ├── StockMapper.java            # Stock mapping
│       │           ├── CurrencyWebMapper.java      # Currency web mapping
│       │           ├── ExchangeWebMapper.java      # Exchange web mapping
│       │           ├── StockTypeWebMapper.java     # Stock type web mapping
│       │           └── ErrorMapper.java            # Error mapping
│       └── outgoing/                         # Output Adapters (External Systems)
│           ├── client/                      # External API Clients
│           │   ├── MarketDataAdapter.java           # Market data integration
│           │   ├── TwelveDataClient.java            # TwelveData API client
│           │   ├── dto/                            # External API DTOs
│           │   │   └── TwelveDataStockResponse.java # TwelveData response
│           │   └── mapper/                         # External mapping
│           │       └── StockMapper.java            # Stock data mapping
│           └── repository/                   # Database Repositories
│               ├── DatabaseStockRepository.java     # Stock data access
│               ├── DatabaseCurrencyRepository.java  # Currency data access
│               ├── DatabaseExchangeRepository.java  # Exchange data access
│               ├── DatabaseStockTypeRepository.java # Stock type data access
│               ├── mapper/                         # Database Mappers
│               │   ├── StockMapper.java            # Stock entity mapping
│               │   └── CategoryMapper.java         # Category mapping
│               └── persistence/                    # Persistence Layer
│                   ├── StockPersistenceAdapter.java        # Stock persistence
│                   ├── CurrencyPersistenceAdapter.java     # Currency persistence
│                   ├── ExchangePersistenceAdapter.java     # Exchange persistence
│                   ├── StockTypePersistenceAdapter.java    # Stock type persistence
│                   └── entity/                            # JPA Entities
│                       ├── StockEntity.java               # Stock table
│                       ├── CurrencyEntity.java            # Currency table
│                       ├── ExchangeEntity.java            # Exchange table
│                       └── StockTypeEntity.java           # Stock type table
└── SuggestionsApplication.java               # Main Application Class

src/main/resources/
├── application.properties                    # Configuration
└── db/                                      # Database Schema
    ├── changelog.yaml                       # Liquibase master changelog
    └── changelog/                          # Individual changesets
        ├── 001-create-stocks-table.yaml    # Stock table creation
        └── 002-create-categories-tables.yaml # Category tables creation

src/test/java/                              # Test Suite
└── com/portfolio/management/
    ├── application/service/                 # Service Tests
    ├── infrastructure/adapters/
    │   ├── incoming/web/                    # Controller Tests
    │   └── outgoing/
    │       ├── client/                      # Client Tests
    │       └── repository/                  # Repository Tests
    └── domain/                             # Domain Tests

Key Design Patterns

  • Clean Architecture: Clear separation between domain, application, and infrastructure
  • Hexagonal Architecture: Domain isolated from external concerns via ports and adapters
  • Repository Pattern: Data access abstraction with reactive implementations
  • Mapper Pattern: Type-safe mapping between layers using MapStruct
  • Use Case Pattern: Business operations encapsulated as discrete use cases
  • Adapter Pattern: External systems integrated via adapters

Error Handling

The API provides comprehensive error handling with meaningful HTTP status codes and detailed error messages:

HTTP Status Codes

  • 200 OK: Successful request
  • 400 Bad Request: Invalid request parameters or validation errors
  • 404 Not Found: Resource not found
  • 500 Internal Server Error: System errors

Error Response Format

{
  "errors": [
    {
      "field": "query",
      "message": "Search query must be at least 2 characters long",
      "code": "INVALID_QUERY_LENGTH"
    }
  ],
  "message": "Validation failed",
  "timestamp": 1694771234567
}

Validation Rules

  • Query parameters validated for length and format
  • Limit parameters bounded to reasonable ranges
  • Required fields enforced at API level
  • Business rules enforced in domain layer

Future Enhancements

Immediate Roadmap

  • Real-time Updates: WebSocket support for live market data
  • Enhanced Search: Full-text search with Elasticsearch integration
  • User Management: Authentication and user-specific portfolios
  • Rate Limiting: API throttling and quota management
  • Metrics & Monitoring: Prometheus metrics and health checks

Advanced Features

  • Market Data Streaming: Real-time price feeds integration
  • Portfolio Analytics: Performance calculations and reporting
  • Multi-tenancy: Support for multiple organizations
  • Event Sourcing: Audit trail for all data changes
  • GraphQL API: Alternative query interface

Infrastructure

  • Kubernetes Deployment: Production-ready K8s manifests
  • CI/CD Pipeline: Automated testing and deployment
  • Service Mesh: Istio integration for microservices
  • Distributed Caching: Redis cluster for high availability
  • Message Queues: Apache Kafka for event-driven architecture

License

© 2025 Pablo Cazorla. All rights reserved. This code is proprietary and confidential.

About

Suggestions API for my portfolio management assistant APP

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors