Skip to content

πŸš€ frameworks-common-java

A modular, extensible Java framework for building robust, scalable microservices and backend systems.
This repository provides shared libraries and utilities for database access, REST APIs, exception handling, caching, messaging, and moreβ€”designed for rapid development and consistency across all OlannaTech Java projects.


πŸ“¦ Modules Overview

Module Name Description
common-database Advanced JPA repositories, dynamic queries, tenant support, and projection utilities.
common-rest REST controller base classes, global exception handling, and response DTOs.
common-shared Shared DTOs, error response models, and utility classes.
common-cache Centralized, two-level (local + Redis) caching with AOP support.
common-auth JWT-based authentication, security config, and endpoint protection.
common-rabbitmq RabbitMQ configuration, integration, and Vault support.

βš™οΈ Module Use Cases & Configuration

1. common-database

Use Cases: - Dynamic field filtering and search - Tenant-aware repositories - DTO and interface-based projections - Integration with Testcontainers for DB testing

Configuration: - Add as a dependency in your module's build.gradle. - Use CustomJpaRepository as your repository base interface. - Example:

public interface UserRepository extends CustomJpaRepository<User, Long> {}
- For tenant support, ensure your entities have a tenant field and use filters like filters.put("tenant.tenantId", tenantId);.


2. common-rest

Use Cases: - Base REST controllers with versioned paths - Global exception handling for clean error responses - Standardized DTOs for API responses

Configuration: - Extend BaseController for all REST controllers:

@RestController
@RequestMapping("/api/v1")
public abstract class BaseController {}
- Use provided DTOs and error models for consistent API responses.


3. common-shared

Use Cases: - Shared DTOs and utility classes across modules - Standard error response models - Common enums and constants

Configuration: - Import shared DTOs and utilities as needed in your modules. - Use error models for consistent error handling.


4. common-cache

Use Cases: - Two-level caching (local + Redis) - AOP-driven cache annotations: @CentralCacheable, @CentralCachePut, @CentralCacheEvict - Distributed locking with Redisson (with fallback to local lock)

Configuration: - Add to your dependencies. - In application.properties or application.yml:

spring.data.redis.host=localhost
spring.data.redis.port=6379
spring.data.redis.password=your_secure_password
- Use cache annotations:
@CentralCacheable(value = "userCache", key = "#userId")
public UserDto getUser(Long userId) { ... }
- If Redis is unavailable, the system will fallback to local cache and lock service automatically.

Lock Service Usage: - The LockService bean provides distributed locking via Redis (Redisson) or local fallback. - Inject and use it in your service:

@Autowired
private LockService lockService;

public void doWithLock(String lockKey, Runnable action) {
    var lock = lockService.getLock(lockKey);
    try {
        if (lock.tryLock(5, 30, TimeUnit.SECONDS)) {
            try {
                action.run();
            } finally {
                lock.unlock();
            }
        } else {
            throw new IllegalStateException("Could not acquire lock");
        }
    } catch (InterruptedException e) {
        Thread.currentThread().interrupt();
        throw new RuntimeException("Lock interrupted", e);
    }
}
- The lock service will use Redis if available, otherwise it will fallback to a local lock implementation, ensuring your application remains resilient.


5. common-auth

Use Cases: - JWT-based authentication and authorization - Security configuration for endpoints - User context extraction

Configuration: - Add as a dependency. - Configure JWT secret and expiration in your properties:

security.jwt.secret=your_jwt_secret
security.jwt.expiration=3600
- Use provided security config and filters in your Spring Security setup.


6. common-rabbitmq

Use Cases: - Centralized RabbitMQ configuration - Secure credential loading from Vault - Queue, exchange, and routing key management

Configuration: - Add as a dependency. - In application.yml:

spring:
  rabbitmq:
    host: localhost
    port: 5672
    username: guest
    password: guest
vault:
  rabbitmq:
    credentials-path: secret/data/rabbitmq
- Use provided configuration classes for queue and exchange setup.

Producer Example:

@Autowired
private EventPublisher eventPublisher;

public void publishOrderCreated(OrderCreatedEvent event) {
    // Publishes to the target service's exchange and routing key
    eventPublisher.publishEvent(event, "order-service");
    // Or to your own service's exchange:
    eventPublisher.publishEvent(event);
}

Consumer Example:

@Component
public class OrderCreatedEventHandler implements EventHandler<OrderData> {
    @Override
    public void handleEvent(BaseEvent<?> event) {
        OrderData data = (OrderData) event.getPayload();
        // handle order created event
    }
}

// Registration (e.g. in a @PostConstruct of a @Configuration class):
@Autowired
private EventConsumer eventConsumer;
@Autowired
private OrderCreatedEventHandler handler;

@PostConstruct
public void registerHandlers() {
    eventConsumer.registerHandler("ORDER_CREATED", handler, OrderData.class);
}

  • The consumer will automatically dispatch incoming events of type ORDER_CREATED to your handler.
  • Handlers can be registered for any event type and payload.

πŸ—οΈ Architecture Highlights

  • Reusable JPA Repositories: Dynamic field and search queries, projection support, and tenant-aware methods.
  • REST API Foundation: Base controllers, global exception handler, and standardized error responses.
  • DTO & Projection Support: Easily map entities to DTOs or interfaces for efficient data transfer.
  • Centralized Caching: Two-level cache (local + Redis) with AOP annotations for easy integration.
  • Testcontainers Integration: Seamless database testing with isolated PostgreSQL containers.
  • Nexus Publishing: Ready-to-use Gradle publishing configuration for internal Maven repositories.
  • RabbitMQ & Vault: Secure, dynamic messaging configuration with Vault integration.

πŸ“š Usage Guide

1. Database Access with CustomJpaRepository

Features: - Dynamic field filtering (supports nested fields, e.g., "tenant.tenantId") - Search with pagination - Projection to DTOs or interfaces

Example Entity:

@Entity
public class User {
    @Id private Long id;
    private String username;
    private String email;
    @ManyToOne private Tenant tenant;
}

Example DTO:

public record UserSummaryDto(Long id, String username, String email, String tenantId) {}

Repository:

public interface UserRepository extends CustomJpaRepository<User, Long> {}

Service Usage:

@Autowired UserRepository userRepository;

public Page<UserSummaryDto> searchUsers(String search, String tenantId, Pageable pageable) {
    Map<String, Object> filters = new HashMap<>();
    filters.put("tenant.tenantId", tenantId);
    return userRepository.findByFieldsAndSearch(filters, search, pageable, UserSummaryDto.class);
}


2. REST API Foundation

Base Controller:

@RestController
@RequestMapping("/api/v1")
public abstract class BaseController {}

All controllers extending BaseController will have /api/v1 prepended to their paths.


3. Global Exception Handling

GlobalExceptionHandler provides: - User-friendly validation error messages - Clean SQL constraint violation messages (e.g., "A record with email 'foo@bar.com' already exists.") - Consistent error response structure

Example Error Response:

{
  "timestamp": "2025-04-22T12:34:56Z",
  "status": "BAD_REQUEST",
  "message": "Validation failed",
  "errorDetails": "[email: must not be blank (rejected value: null)]",
  "path": "/api/v1/users"
}


4. Centralized Caching

  • Use @CentralCacheable, @CentralCachePut, and @CentralCacheEvict annotations for easy, AOP-driven caching.
  • Supports two-level cache (local + Redis) for high performance and resilience.
  • Example annotation:
    @CentralCacheable(value = "userCache", key = "#userId")
    public UserDto getUser(Long userId) { ... }
    

5. Testing with Testcontainers

DatabaseApplicationTests uses Testcontainers to spin up a PostgreSQL instance for integration tests:

static final PostgreSQLContainer<?> postgresContainer = new PostgreSQLContainer<>("postgres:15.3")
    .withDatabaseName("testdb")
    .withUsername("test")
    .withPassword("test");

Ensures your tests run in a real, isolated database environment.


6. Gradle Publishing & Dependency Management

Publishing to Nexus: - Configured for both release and snapshot repositories. - Credentials and URLs are parameterized for security and flexibility.

Reimporting/Cleaning Dependencies:

./gradlew clean build --refresh-dependencies
or, for a hard reset:
rm -rf ~/.gradle/caches
./gradlew clean build --refresh-dependencies


7. RabbitMQ & Vault Integration

  • Securely load RabbitMQ credentials from Vault at runtime.
  • Centralized configuration for queues, exchanges, and routing keys.

πŸ›‘οΈ Error Handling Patterns

  • Validation errors: Clearly list all field errors with rejected values.
  • SQL constraint violations: Parse and present a user-friendly message.
  • Other exceptions: Return a generic, safe message with details for debugging.

🧩 Extending the Framework

  • Add new projections: Create DTOs with constructors matching selected fields.
  • Add new modules: Follow the modular structure for easy integration.
  • Customize error handling: Extend GlobalExceptionHandler as needed.

πŸ“ Contributing

  1. Fork the repo and create your feature branch.
  2. Write clear, tested code and update documentation.
  3. Submit a pull request!

πŸ’‘ Tips

  • Use nested field names (e.g., "tenant.tenantId") in filters for deep queries.
  • Always match DTO constructor parameters to the fields you select in queries.
  • For interface-based projections, use Spring Data repository methods.

πŸ“– Example API Call

Search users by tenant and name:

GET /api/v1/users/search?tenantId=abc123&search=john&page=0&size=10


πŸ‘₯ Maintainers


🏷️ License

MIT License


frameworks-common-java: Your foundation for fast, reliable, and maintainable Java microservices.