The UTMStack backend is built on Spring Boot, utilizing a robust initialization sequence that ensures proper environment configuration, optimized database connections, and dynamic module loading. This document outlines the core application entry point, configuration properties, and the module factory design that powers the UTMStack API.
Application Entry Point
The application bootstraps through the UtmstackApp class, which serves as the primary Spring Boot entry point. It is annotated with @SpringBootApplication and explicitly enables configuration properties for Liquibase and custom application settings.
Profile Validation
During the @PostConstruct phase, the application performs strict validation on the active Spring profiles to prevent conflicting environments from running simultaneously.
The application will log a severe error if it detects mutually exclusive profiles. Specifically, you cannot run dev and prod together, nor can you run dev and cloud together.
Spring Boot loads the environment variables and active profiles passed via command-line arguments (e.g., --spring.profiles.active=dev).
The initApplication() method checks for conflicting profile combinations using JHipsterConstants.
SpringApplication.run() executes, initializing the IoC container, beans, and embedded web server.
The logApplicationStartup() method determines the protocol (HTTP/HTTPS based on SSL keystore presence), port, and context path, then logs the local and external access URLs.
Configuration Settings
The core backend configuration is defined in application.yml. It establishes the baseline behavior for the application, including monitoring, database interactions, and async task execution.
Management and Metrics
UTMStack exposes comprehensive health and metric data via Spring Boot Actuator, mapped to the /management base path.
Health Probes: Liveness and readiness probes are enabled (
/management/health/livenessand/management/health/readiness), which are essential for Kubernetes deployments.Prometheus Integration: Metrics are exported to Prometheus with a 60-second step interval. Percentile histograms are enabled across all metrics (0.5, 0.75, 0.95, 0.99) to provide accurate latency tracking.
Security: The health endpoint details are restricted (
show-details: when_authorized) to users withROLE_ADMINorROLE_USER.
JPA and Hibernate Optimization
The data access layer is heavily optimized for performance and consistency.
hibernate.ddl-auto is explicitly set to none. UTMStack relies entirely on Liquibase for database schema management and migrations, preventing Hibernate from making unintended schema changes during startup.
UTMStack configures dedicated thread pools to isolate background tasks from standard HTTP request processing:
spring:
task:
execution:
thread-name-prefix: utm-stack-cloud-backend-task-
pool:
core-size: 2
max-size: 1000
queue-capacity: 10000
scheduling:
thread-name-prefix: utmstack-api-scheduling-
pool:
size: 2This ensures that scheduled jobs and @Async executions do not exhaust the Tomcat worker threads.
Module Factory Architecture
To maintain a clean, decoupled architecture, UTMStack utilizes a Factory Pattern for its application modules. This allows the system to dynamically resolve and instantiate specific business modules at runtime without hardcoding dependencies.
Implementation Details
The ModuleFactory relies on Spring's dependency injection to automatically discover all beans implementing the IModule interface.
@Component
@RequiredArgsConstructor
public class ModuleFactory {
// Spring automatically injects all beans implementing IModule
private final Map<String, IModule> moduleBeans;
private Map<ModuleName, IModule> moduleMap;
@PostConstruct
void init() {
// Remap the injected beans using their specific ModuleName enum as the key
moduleMap = moduleBeans.values().stream()
.collect(Collectors.toMap(IModule::getName, Function.identity()));
}
public IModule getInstance(ModuleName nameShort) {
IModule module = moduleMap.get(nameShort);
if (module == null) {
throw new IllegalArgumentException("Unrecognized module: " + nameShort.name());
}
return module;
}
}Adding a new module: To register a new module in the system, simply create a class that implements IModule, annotate it with @Component (or @Service), and ensure its getName() method returns a valid ModuleName enum. The ModuleFactory will automatically wire it during the @PostConstruct phase.