Skip to content

bytememiles/java-transaction

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

9 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Transaction System

A complete product transaction system built with Spring Boot, implementing user and merchant modules for product purchases with prepaid cash accounts.

Features

  • User Module: User management, prepaid account management, account recharge via mocked payment gateway
  • Merchant Module: Merchant management, product catalog, inventory management with audit trails
  • Transaction Module: Order processing with atomic transactions (deducts user balance, credits merchant, deducts inventory)
  • Reconciliation Module: Daily scheduled job to reconcile merchant account balance with calculated sales value
  • Payment Gateway: Mocked REST API for account recharge (extensible for real banking APIs)
  • Audit Trails: Complete transaction history for account and inventory changes
  • Optimistic Locking: Concurrency control for balance and inventory updates
  • RESTful API: Comprehensive REST API with Swagger/OpenAPI documentation

Technology Stack

  • Java: 17
  • Spring Boot: 3.2.0
  • Spring Cloud: 2023.0.0
  • PostgreSQL: 15 (via Docker)
  • Flyway: Database migration
  • Lombok: Boilerplate reduction
  • Swagger/OpenAPI: API documentation (SpringDoc OpenAPI 3)
  • Spring Retry: Retry logic for optimistic locking failures
  • JUnit 5 & Mockito: Unit testing
  • Testcontainers: Integration testing with real PostgreSQL
  • JaCoCo: Code coverage reporting

Architecture

The system follows Domain-Driven Design (DDD) principles with clear boundary contexts:

  • User Context: User and Account aggregates
  • Merchant Context: Merchant, Product, and Inventory aggregates
  • Transaction Context: Order and Payment aggregates
  • Reconciliation Context: Reconciliation reports
  • External Integration Context: Payment gateway adapters

Project Structure

src/main/java/com/mamoru/transactionsystem/
├── common/                    # Common utilities
│   ├── config/               # Configuration classes
│   ├── dto/                  # Shared DTOs
│   └── exception/            # Exception handling
├── user/                      # User module
│   ├── domain/               # Domain entities
│   ├── application/          # Application services
│   ├── infrastructure/      # Repositories
│   └── presentation/         # REST controllers
├── merchant/                  # Merchant module
├── transaction/               # Transaction module
├── reconciliation/            # Reconciliation module
└── payment-gateway/           # Payment gateway module

Prerequisites

  • Java 17 or higher
  • Maven 3.6+
  • Docker and Docker Compose
  • IDE (IntelliJ IDEA, Eclipse, or VS Code)

Setup Instructions

1. Clone and Navigate

cd /home/dev/Dev/Assessments/Mamoru

2. Start PostgreSQL Database

docker-compose up -d

This will start PostgreSQL on port 5432 with:

  • Database: transaction_system
  • Username: postgres
  • Password: postgres

3. Build the Project

mvn clean install

4. Run the Application

mvn spring-boot:run

Or from your IDE, run the TransactionSystemApplication class.

The application will start on http://localhost:8080

5. Access API Documentation

Once the application is running, access Swagger UI at:

API Endpoints

All endpoints are documented in Swagger UI. Access at: http://localhost:8080/swagger-ui.html

User Management APIs

Base Path: /api/v1/users

  • POST /api/v1/users - Create a new user (automatically creates prepaid account)
  • GET /api/v1/users/{userId} - Get user details by ID
  • POST /api/v1/users/{userId}/accounts/recharge - Recharge user prepaid account via mocked payment gateway
  • GET /api/v1/users/{userId}/accounts/balance - Get current account balance

Merchant Management APIs

Base Path: /api/v1/merchants

  • POST /api/v1/merchants - Create a new merchant
  • GET /api/v1/merchants/{merchantId} - Get merchant details (including account balance)

Products (Base Path: /api/v1/merchants/{merchantId}/products)

  • POST /api/v1/merchants/{merchantId}/products - Create a new product
  • GET /api/v1/merchants/{merchantId}/products - Get all products for a merchant
  • GET /api/v1/merchants/{merchantId}/products/{productId} - Get product details by ID

Inventory (Base Path: /api/v1/merchants/{merchantId}/inventory)

  • POST /api/v1/merchants/{merchantId}/inventory/products/{productId}/add - Add quantity to inventory
  • GET /api/v1/merchants/{merchantId}/inventory - Get all inventory records for a merchant
  • GET /api/v1/merchants/{merchantId}/inventory/products/{productId} - Get inventory for a specific product

Transaction APIs

Base Path: /api/v1/orders

  • POST /api/v1/orders - Place an order (requires X-User-Id header)
    • Atomically: deducts user balance, credits merchant, deducts inventory
  • GET /api/v1/orders/{orderId} - Get order details by order ID
  • GET /api/v1/orders/order-number/{orderNumber} - Get order details by order number

Reconciliation APIs

Base Path: /api/v1/reconciliation/merchants/{merchantId}

  • POST /api/v1/reconciliation/merchants/{merchantId}/run - Manually trigger reconciliation for yesterday
  • POST /api/v1/reconciliation/merchants/{merchantId}/run/{reportDate} - Manually trigger reconciliation for specific date (format: yyyy-MM-dd)
  • GET /api/v1/reconciliation/merchants/{merchantId}/reports - Get all reconciliation reports for a merchant
  • GET /api/v1/reconciliation/merchants/{merchantId}/reports/{reportDate} - Get reconciliation report for specific date (format: yyyy-MM-dd)

Configuration

The application uses a .env file for environment-based configuration. This allows you to configure the application without modifying code files.

Setting Up Environment Variables

  1. Copy the example file:

    cp .env.example .env
  2. Edit .env file with your configuration values:

    # Database Configuration
    DB_HOST=localhost
    DB_PORT=5432
    DB_NAME=transaction_system
    DB_USERNAME=postgres
    DB_PASSWORD=postgres
    
    # Application Configuration
    SERVER_PORT=8080
    APP_NAME=transaction-system
    
    # ... and more
  3. The .env file is git-ignored - your local configuration won't be committed to the repository.

Configuration Variables

Database Configuration

  • DB_HOST - Database host (default: localhost)
  • DB_PORT - Database port (default: 5432)
  • DB_NAME - Database name (default: transaction_system)
  • DB_USERNAME - Database username (default: postgres)
  • DB_PASSWORD - Database password (default: postgres)

Docker Configuration

  • DB_CONTAINER_NAME - Docker container name for PostgreSQL (default: transaction-system-db)

Application Configuration

  • SERVER_PORT - Server port (default: 8080)
  • APP_NAME - Application name (default: transaction-system)

Reconciliation

  • RECONCILIATION_ENABLED - Enable/disable reconciliation (default: true)
  • RECONCILIATION_CRON - Cron schedule (default: 0 0 2 * * * - daily at 2 AM)
  • DEFAULT_CURRENCY - Default currency (default: USD)

Logging

  • LOG_LEVEL_ROOT - Root log level (default: INFO)
  • LOG_LEVEL_APP - Application log level (default: DEBUG)
  • LOG_SQL_ENABLED - Enable SQL logging (default: false)

JPA/Hibernate

  • JPA_DDL_AUTO - DDL mode (default: validate)
  • JPA_SHOW_SQL - Show SQL queries (default: false)
  • JPA_BATCH_SIZE - Batch size (default: 20)

Connection Pool (HikariCP)

  • HIKARI_MAX_POOL_SIZE - Maximum pool size (default: 10)
  • HIKARI_MIN_IDLE - Minimum idle connections (default: 5)
  • HIKARI_CONNECTION_TIMEOUT - Connection timeout in ms (default: 30000)

Flyway

  • FLYWAY_ENABLED - Enable Flyway migrations (default: true)

Swagger/OpenAPI

  • SWAGGER_ENABLED - Enable Swagger UI (default: true)
  • SWAGGER_UI_PATH - Swagger UI path (default: /swagger-ui.html)

Configuration Priority

Configuration is loaded in the following order (highest priority first):

  1. System environment variables
  2. .env file (project root)
  3. application.yml defaults

Docker Compose Integration

The docker-compose.yml file automatically loads variables from .env, so your database configuration will be consistent across the application and Docker containers.

Testing

Test Structure

The project includes comprehensive unit and integration tests:

Unit Tests:

  • Domain entity tests (Account, Merchant, Inventory, Product, Order)
  • Service layer tests (UserService, OrderService, InventoryService)
  • Business logic validation and edge cases

Integration Tests:

  • Repository integration tests (using Testcontainers with real PostgreSQL)
  • Controller integration tests (full end-to-end API testing with MockMvc)

Run All Tests

mvn test

This runs all unit tests by default. Integration tests are excluded by default (require Docker).

Run Integration Tests

Integration tests require Docker to be running and accessible. To run all tests including integration tests:

mvn test -Dsurefire.excludedGroups=

Note: Make sure Docker is running and your user has permission to access Docker. If you get permission errors, see Docker Permission Issues in the SETUP_GUIDE.md.

Run Tests with Coverage

mvn test jacoco:report

Coverage report will be generated at: target/site/jacoco/index.html

Test Coverage

The project targets 80% unit test coverage for core business logic:

  • Domain entities: ✅ Covered
  • Application services: ✅ Covered
  • Controllers: ✅ Integration tests included
  • Repositories: ✅ Integration tests included

Test Configuration

  • Unit Tests: Run by default with mvn test
  • Integration Tests: Excluded by default, require Docker. Run with mvn test -Dsurefire.excludedGroups=
  • Testcontainers: Real PostgreSQL container for integration tests
  • Test Profile: Uses application-test.yml with test-specific configuration
  • Flyway: Disabled in tests (uses ddl-auto: create-drop instead)

Database Migration

Database schema is managed by Flyway. Migrations are located in:

  • src/main/resources/db/migration/

The initial schema (V1__initial_schema.sql) creates all necessary tables:

  • Users and Accounts
  • Merchants, Products, and Inventory
  • Orders and Payments
  • Reconciliation Reports

Flyway Migration Commands

Flyway runs automatically when the application starts, but you can also run migrations manually using Maven commands:

Run Migrations

# Run all pending migrations
mvn flyway:migrate

# Run with custom database settings (if not using .env)
mvn flyway:migrate -DDB_HOST=localhost -DDB_PORT=5432 -DDB_NAME=transaction_system -DDB_USERNAME=postgres -DDB_PASSWORD=postgres

Check Migration Status

# Check which migrations have been applied
mvn flyway:info

Validate Migrations

# Validate that migrations haven't been modified
mvn flyway:validate

Repair Migrations

# Repair the Flyway schema history table if it's corrupted
mvn flyway:repair

Baseline Database

# Mark an existing database as baseline (for databases that existed before Flyway)
mvn flyway:baseline

Clean Database (⚠️ Use with caution!)

# Drops all database objects (WARNING: This will delete all data!)
mvn flyway:clean

Creating New Migrations

  1. Create a new migration file in src/main/resources/db/migration/:

    • Naming convention: V{version}__{description}.sql
    • Example: V2__add_user_profile_table.sql
    • Example: V3__add_indexes.sql
  2. Migration files are versioned - Flyway tracks which migrations have been applied

  3. Example migration file:

    -- V2__add_user_profile_table.sql
    CREATE TABLE user_profiles (
        id BIGSERIAL PRIMARY KEY,
        user_id BIGINT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
        first_name VARCHAR(100),
        last_name VARCHAR(100),
        phone VARCHAR(20),
        created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
        updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL
    );

Migration Best Practices

  • Never modify existing migration files after they've been applied to production
  • Use version numbers sequentially (V1, V2, V3, ...)
  • Test migrations on a copy of production data before applying
  • Use descriptive names for migration files
  • Keep migrations small and focused - one logical change per migration
  • Always backup the database before running migrations in production

Environment Variables

Migration commands respect your .env file configuration:

  • DB_HOST - Database host
  • DB_PORT - Database port
  • DB_NAME - Database name
  • DB_USERNAME - Database username
  • DB_PASSWORD - Database password

Troubleshooting Migrations

Check if Migration Files are Being Found

# Check if migration files exist in source
ls -la src/main/resources/db/migration/

# Check if migration files are in target (after compile)
ls -la target/classes/db/migration/

# If files are missing from target, rebuild:
mvn clean compile

Verify Database Schema State

# Check Flyway migration history
echo "password" | sudo -S docker exec transaction-system-db psql -U postgres -d transaction_system -c "SELECT version, description, installed_on, success FROM flyway_schema_history ORDER BY installed_rank;"

# Check if tables exist
echo "password" | sudo -S docker exec transaction-system-db psql -U postgres -d transaction_system -c "\dt"

# Check column types (verify UUID conversion)
echo "password" | sudo -S docker exec transaction-system-db psql -U postgres -d transaction_system -c "SELECT table_name, column_name, data_type, column_default FROM information_schema.columns WHERE table_schema = 'public' AND column_name = 'id' ORDER BY table_name;"

Fix Missing Migration Files

If Flyway shows "No migrations found":

# 1. Clean and rebuild to copy resources
mvn clean compile

# 2. Verify files are in target
ls target/classes/db/migration/

# 3. Run migration
mvn flyway:migrate

Remove a Migration from History (if file was deleted)

If you deleted a migration file but it's still in the database history:

# Check current migration history
echo "password" | sudo -S docker exec transaction-system-db psql -U postgres -d transaction_system -c "SELECT version, description FROM flyway_schema_history;"

# Remove a specific migration from history (replace '3' with the version to remove)
# WARNING: Only do this if the migration file no longer exists
echo "password" | sudo -S docker exec transaction-system-db psql -U postgres -d transaction_system -c "DELETE FROM flyway_schema_history WHERE version = '3';"

Verify UUID Columns are Working

# Check users table structure
echo "password" | sudo -S docker exec transaction-system-db psql -U postgres -d transaction_system -c "\d users"

# Check if any IDs are NULL
echo "password" | sudo -S docker exec transaction-system-db psql -U postgres -d transaction_system -c "SELECT COUNT(*) as total_users, COUNT(id) as users_with_id, COUNT(*) FILTER (WHERE id IS NULL) as null_ids FROM users;"

# View sample data
echo "password" | sudo -S docker exec transaction-system-db psql -U postgres -d transaction_system -c "SELECT id, username FROM users LIMIT 5;"

Reset Database (Development Only)

If you need to completely reset the database:

# Option 1: Use Flyway clean (drops all objects)
mvn flyway:clean
mvn flyway:migrate

# Option 2: Drop and recreate database
echo "password" | sudo -S docker exec transaction-system-db psql -U postgres -c "DROP DATABASE IF EXISTS transaction_system;"
echo "password" | sudo -S docker exec transaction-system-db psql -U postgres -c "CREATE DATABASE transaction_system;"
mvn flyway:migrate

Check for Index Conflicts

If you see index-related errors:

# List all indexes
echo "password" | sudo -S docker exec transaction-system-db psql -U postgres -d transaction_system -c "\di"

# Check specific index
echo "password" | sudo -S docker exec transaction-system-db psql -U postgres -d transaction_system -c "\d+ orders"

Common Issues and Solutions

Issue: "No migrations found"

  • Solution: Run mvn clean compile first, then mvn flyway:migrate

Issue: "Migration V3 already applied but file missing"

  • Solution: Remove from history: DELETE FROM flyway_schema_history WHERE version = '3';

Issue: "Index already exists" warnings

  • Solution: This is normal if using CREATE INDEX IF NOT EXISTS. The warnings are harmless.

Issue: "Column type mismatch"

  • Solution: Verify migration V2 was applied. Check with: SELECT data_type FROM information_schema.columns WHERE table_name = 'users' AND column_name = 'id';

Database Seeding

The project includes a seeding script to populate the database with sample data for development and testing.

Seed Data Includes

  • 5 Sample Users with accounts and initial balances:

    • john_doe - $1000.00
    • jane_smith - $500.00
    • bob_wilson - $750.00
    • alice_brown - $2000.00
    • charlie_davis - $100.00
  • 4 Sample Merchants:

    • Tech Store
    • Fashion Boutique
    • Electronics Hub
    • Book Emporium
  • Multiple Products across all merchants with inventory

  • Sample Account Transactions (recharges)

Running the Seed Script

Option 1: Using Shell Script (Recommended)

./scripts/seed-database.sh

The script will:

  • Check database connection
  • Ask for confirmation
  • Run the seeding SQL script
  • Display a summary

Option 2: Using Maven

# Run the shell script via Maven
mvn exec:exec@seed-database

# Or run SQL directly via Maven
mvn sql:execute@seed-database-sql

Option 3: Using psql Directly

# Load environment variables from .env
export $(grep -v '^#' .env | xargs)

# Run the seed script
psql -h ${DB_HOST:-localhost} -p ${DB_PORT:-5432} -U ${DB_USERNAME:-postgres} -d ${DB_NAME:-transaction_system} -f src/main/resources/db/seeds/seed_data.sql

Seed Script Location

The seed script is located at:

  • src/main/resources/db/seeds/seed_data.sql

Notes

  • The seed script uses ON CONFLICT DO NOTHING / ON CONFLICT DO UPDATE to be idempotent
  • You can run it multiple times safely - it will update existing data or skip duplicates
  • To reset seed data, you can clean the database first: mvn flyway:clean (⚠️ WARNING: deletes all data)

Customizing Seed Data

Edit src/main/resources/db/seeds/seed_data.sql to customize:

  • Number of users
  • Initial account balances
  • Merchants and their products
  • Inventory quantities
  • Sample transactions

Development Workflow

  1. Create Feature Branch: git checkout -b feature/your-feature-name
  2. Implement Feature: Follow DDD principles and maintain test coverage
  3. Run Tests: Ensure all tests pass and coverage is maintained
  4. Commit Changes: git commit -m "feat: your feature description"
  5. Push and Create PR: Push to remote and create pull request

Key Features & Design Patterns

Transaction Management

  • Atomic Operations: Order processing ensures all-or-nothing execution
  • Optimistic Locking: Version-based concurrency control for balance and inventory updates
  • Retry Logic: Automatic retry on optimistic locking failures (Spring Retry)

Audit Trails

  • Account Transactions: Complete history of all balance changes (RECHARGE, DEBIT, CREDIT)
  • Inventory Transactions: Complete history of all inventory changes (ADD, DEDUCT)
  • Reference Tracking: All transactions linked to orders or operations

Reconciliation

  • Daily Scheduled Job: Automatically reconciles all merchants at 2 AM
  • Discrepancy Detection: Compares account balance vs calculated sales value
  • Report Generation: Historical reconciliation reports with status (MATCHED/DISCREPANCY)
  • Manual Triggers: API endpoints for on-demand reconciliation

Error Handling

  • Global Exception Handler: Centralized error handling with structured error responses
  • Custom Exceptions: ResourceNotFound, InsufficientBalance, InvalidOperation
  • Validation: Jakarta Bean Validation on all request DTOs

Extensibility

The system is designed to be highly configurable and extensible:

  • Payment Gateway: Interface-based design allows easy integration of real banking APIs (e.g., https://sandbox.bind.com.ar/apidoc/)
  • Configuration: Externalized configuration via application.yml with Spring @ConfigurationProperties
  • Modular Structure: Clear module boundaries enable future microservice split
  • DDD Architecture: Domain-driven design with clear aggregate roots and boundaries
  • Event-Driven Hooks: Ready for Kafka integration if needed
  • Repository Pattern: Easy to swap implementations or add new data sources

Future Enhancements

  • Integration with real banking API (https://sandbox.bind.com.ar/apidoc/)
  • Kafka event-driven architecture
  • Multi-currency support
  • Advanced reporting and analytics
  • WebSocket support for real-time updates

Troubleshooting

Database Connection Issues

If you encounter database connection errors:

  1. Ensure Docker is running: docker ps
  2. Check PostgreSQL container: docker-compose ps
  3. Verify database is accessible: docker exec -it transaction-system-db psql -U postgres -d transaction_system
  4. Check connection settings in application.yml

Port Already in Use

If port 8080 is already in use:

  • Change server.port in application.yml
  • Or stop the conflicting service: lsof -i :8080 or netstat -tulpn | grep 8080

Flyway Migration Issues

If migrations fail:

  • Check database connection
  • Verify migration files are in src/main/resources/db/migration/
  • Check Flyway logs in application output
  • Ensure database is empty or use baseline-on-migrate: true

Swagger UI Not Showing Endpoints

If Swagger UI is empty:

  1. Restart the application (SpringDoc scans at startup)
  2. Check application logs for errors
  3. Verify controllers are in com.mamoru.transactionsystem package
  4. Access Swagger UI at: http://localhost:8080/swagger-ui.html

Test Failures

If tests fail:

  1. Ensure Docker is running (for Testcontainers)
  2. Check test logs for specific errors
  3. Verify test profile is active: @ActiveProfiles("test")
  4. For integration tests, ensure PostgreSQL container starts successfully

Quick Start Example

1. Create a User

curl -X POST http://localhost:8080/api/v1/users \
  -H "Content-Type: application/json" \
  -d '{
    "username": "john_doe",
    "email": "john@example.com"
  }'

2. Recharge Account

curl -X POST http://localhost:8080/api/v1/users/1/accounts/recharge \
  -H "Content-Type: application/json" \
  -d '{
    "amount": 1000.00
  }'

3. Create a Merchant

curl -X POST http://localhost:8080/api/v1/merchants \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Tech Store"
  }'

4. Add a Product

curl -X POST http://localhost:8080/api/v1/merchants/1/products \
  -H "Content-Type: application/json" \
  -d '{
    "sku": "LAPTOP-001",
    "name": "Gaming Laptop",
    "price": 999.99
  }'

5. Add Inventory

curl -X POST http://localhost:8080/api/v1/merchants/1/inventory/products/1/add \
  -H "Content-Type: application/json" \
  -d '{
    "quantity": 10
  }'

6. Place an Order

curl -X POST http://localhost:8080/api/v1/orders \
  -H "Content-Type: application/json" \
  -H "X-User-Id: 1" \
  -d '{
    "merchantId": 1,
    "sku": "LAPTOP-001",
    "quantity": 1
  }'

7. Run Reconciliation

curl -X POST http://localhost:8080/api/v1/reconciliation/merchants/1/run

License

This is an assessment project for technical evaluation purposes.

Contact

For questions or issues, please refer to the assessment requirements document.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published