Skip to content

theChefEngineer/warehouse

Repository files navigation

FX Deals Data Warehouse

A Spring Boot application for Bloomberg to analyze FX deals. The system accepts FX deal details, validates them, prevents duplicate imports, and persists data to PostgreSQL.

Features

  • ✅ REST API for FX deal management
  • ✅ Validation of deal structure and data types
  • ✅ Duplicate deal prevention
  • ✅ No rollback policy - partial success allowed
  • ✅ Comprehensive error handling and logging
  • ✅ Unit tests with coverage reporting
  • ✅ Docker Compose deployment
  • ✅ PostgreSQL database
  • ✅ Makefile for easy commands

Technology Stack

  • Java 17
  • Spring Boot 3.2.0
  • PostgreSQL 15
  • Maven
  • JUnit 5 & Mockito
  • Docker & Docker Compose
  • Lombok

Prerequisites

  • Java 17+
  • Maven 3.6+
  • Docker & Docker Compose
  • Make (optional)

Quick Start

Using Make (Recommended)

# Start application with Docker
make docker-up

# View logs
make docker-logs

# Stop application
make docker-down

# Run tests
make test

# Build project
make build

Manual Setup

# Build the project
./mvnw clean package -DskipTests

# Start with Docker Compose
docker-compose up --build -d

# View logs
docker-compose logs -f app

# Stop services
docker-compose down

API Endpoints

Base URL

http://localhost:8080/api/fx-deals

1. Create Single Deal

POST /api/fx-deals

Request Body:

{
  "dealUniqueId": "DEAL-2024-001",
  "fromCurrencyIsoCode": "USD",
  "toCurrencyIsoCode": "EUR",
  "dealTimestamp": "2024-01-15T10:30:00",
  "dealAmount": 10000.50
}

Response (201 Created):

{
  "id": 1,
  "dealUniqueId": "DEAL-2024-001",
  "fromCurrencyIsoCode": "USD",
  "toCurrencyIsoCode": "EUR",
  "dealTimestamp": "2024-01-15T10:30:00",
  "dealAmount": 10000.50,
  "createdAt": "2024-01-15T10:35:00"
}

2. Import Multiple Deals

POST /api/fx-deals/import

Request Body (Array):

[
  {
    "dealUniqueId": "DEAL-2024-001",
    "fromCurrencyIsoCode": "USD",
    "toCurrencyIsoCode": "EUR",
    "dealTimestamp": "2024-01-15T10:30:00",
    "dealAmount": 10000.50
  },
  {
    "dealUniqueId": "DEAL-2024-002",
    "fromCurrencyIsoCode": "GBP",
    "toCurrencyIsoCode": "JPY",
    "dealTimestamp": "2024-01-15T11:45:00",
    "dealAmount": 25000.75
  }
]

Response (201 Created or 206 Partial Content):

{
  "totalRecords": 2,
  "successCount": 2,
  "failureCount": 0,
  "errors": []
}

3. Get All Deals

GET /api/fx-deals

Response (200 OK):

[
  {
    "id": 1,
    "dealUniqueId": "DEAL-2024-001",
    "fromCurrencyIsoCode": "USD",
    "toCurrencyIsoCode": "EUR",
    "dealTimestamp": "2024-01-15T10:30:00",
    "dealAmount": 10000.50,
    "createdAt": "2024-01-15T10:35:00"
  }
]

Testing the Application

Test with Sample Data

# Import sample deals
curl -X POST http://localhost:8080/api/fx-deals/import \
  -H "Content-Type: application/json" \
  -d @sample-deals.json

# Get all deals
curl http://localhost:8080/api/fx-deals

Create Single Deal

curl -X POST http://localhost:8080/api/fx-deals \
  -H "Content-Type: application/json" \
  -d '{
    "dealUniqueId": "TEST-001",
    "fromCurrencyIsoCode": "USD",
    "toCurrencyIsoCode": "EUR",
    "dealTimestamp": "2024-01-15T10:30:00",
    "dealAmount": 5000.00
  }'

Test Duplicate Prevention

# Try importing the same deal twice
curl -X POST http://localhost:8080/api/fx-deals \
  -H "Content-Type: application/json" \
  -d '{
    "dealUniqueId": "TEST-001",
    "fromCurrencyIsoCode": "USD",
    "toCurrencyIsoCode": "EUR",
    "dealTimestamp": "2024-01-15T10:30:00",
    "dealAmount": 5000.00
  }'

# Expected: 409 Conflict error

Validation Rules

Required Fields

  • dealUniqueId: Must not be blank
  • fromCurrencyIsoCode: Must be 3-character ISO code (A-Z)
  • toCurrencyIsoCode: Must be 3-character ISO code (A-Z)
  • dealTimestamp: Must be valid LocalDateTime
  • dealAmount: Must be greater than 0

Business Rules

  • Deal unique ID must be unique across all deals
  • Currency codes must be exactly 3 uppercase letters
  • Deal amount must be positive
  • Duplicate deals are rejected with 409 Conflict

No Rollback Policy

When importing multiple deals:

  • Each deal is processed independently
  • Valid deals are saved even if others fail
  • Response includes success/failure counts and error details

Running Tests

# Run all tests
./mvnw test

# Run with coverage
./mvnw clean test

# View coverage report
open target/site/jacoco/index.html

Test Coverage

  • Service layer: 90%+
  • Controller layer: 85%+
  • Overall: 80%+

Project Structure

fx-deals-warehouse/
├── src/
│   ├── main/
│   │   ├── java/com/progresssoft/fxdeals/
│   │   │   ├── controller/       # REST controllers
│   │   │   ├── service/          # Business logic
│   │   │   ├── repository/       # Data access
│   │   │   ├── model/            # JPA entities
│   │   │   ├── dto/              # Data transfer objects
│   │   │   ├── exception/        # Custom exceptions & handlers
│   │   │   └── FxDealsWarehouseApplication.java
│   │   └── resources/
│   │       └── application.properties
│   └── test/
│       ├── java/com/progresssoft/fxdeals/
│       │   ├── controller/       # Controller tests
│       │   └── service/          # Service tests
│       └── resources/
│           └── application.properties
├── docker-compose.yml
├── Dockerfile
├── Makefile
├── pom.xml
├── sample-deals.json
└── README.md

Database Schema

CREATE TABLE fx_deals (
    id BIGSERIAL PRIMARY KEY,
    deal_unique_id VARCHAR(100) UNIQUE NOT NULL,
    from_currency_iso_code VARCHAR(3) NOT NULL,
    to_currency_iso_code VARCHAR(3) NOT NULL,
    deal_timestamp TIMESTAMP NOT NULL,
    deal_amount DECIMAL(19,4) NOT NULL,
    created_at TIMESTAMP NOT NULL
);

CREATE UNIQUE INDEX idx_deal_id ON fx_deals(deal_unique_id);

Error Handling

Validation Errors (400 Bad Request)

{
  "timestamp": "2024-01-15T10:30:00",
  "status": 400,
  "errors": {
    "dealUniqueId": "Deal Unique ID is required",
    "fromCurrencyIsoCode": "From Currency must be 3 characters ISO code"
  }
}

Duplicate Deal (409 Conflict)

{
  "status": 409,
  "message": "Deal with ID DEAL-2024-001 already exists",
  "timestamp": "2024-01-15T10:30:00"
}

Server Error (500 Internal Server Error)

{
  "status": 500,
  "message": "An unexpected error occurred",
  "timestamp": "2024-01-15T10:30:00"
}

Logging

Application logs include:

  • Request processing
  • Validation errors
  • Duplicate deal attempts
  • Database operations
  • Exception stack traces

View logs:

# Docker logs
make docker-logs

# Or
docker-compose logs -f app

Development

Running Locally (Without Docker)

  1. Start PostgreSQL:
docker run -d \
  --name postgres \
  -e POSTGRES_DB=fxdeals \
  -e POSTGRES_USER=postgres \
  -e POSTGRES_PASSWORD=postgres \
  -p 5432:5432 \
  postgres:15-alpine
  1. Run application:
./mvnw spring-boot:run

Building JAR

./mvnw clean package
java -jar target/fx-deals-warehouse-1.0.0.jar

Troubleshooting

Port Already in Use

# Check what's using port 8080
lsof -i :8080

# Kill process
kill -9 <PID>

Database Connection Issues

# Check if PostgreSQL is running
docker ps | grep postgres

# Restart database
docker-compose restart postgres

Clean Everything

make clean
# Or
docker-compose down -v
./mvnw clean

Performance Considerations

  • Database index on deal_unique_id for fast duplicate checks
  • Batch import uses independent transactions (REQUIRES_NEW)
  • Connection pooling via HikariCP
  • Lazy loading for JPA entities

Security Notes

For production deployment:

  • Change default database credentials
  • Enable HTTPS/TLS
  • Add authentication/authorization
  • Implement rate limiting
  • Use secrets management
  • Enable CORS only for trusted origins

License

This project is developed for ProgressSoft Corporation assessment.

Contact

For questions or issues, contact Human Capital Department at ProgressSoft Corporation.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors