Config Service Architecture
The Config Service is the centralized configuration management backbone of the MATIH platform. Running on port 8888, it provides dynamic configuration entries, feature flags with targeting rules, A/B testing experiments, secret management, approval workflows, environment promotion pipelines, bulk import/export, scheduled flag changes, configuration audit trails, and distributed cache management. Every service in the platform retrieves its runtime configuration from this service and receives real-time updates when values change.
Service Overview
| Property | Value |
|---|---|
| Service Name | config-service |
| Port | 8888 |
| Technology | Spring Boot 3.2, Java 21, Spring Cloud Config |
| Database | PostgreSQL (JPA/Hibernate) |
| Cache | Redis (distributed config cache) |
| Event Bus | Kafka (config change events) |
| Locking | Redis distributed locks |
| API Documentation | OpenAPI 3.0 (Swagger) |
Architecture Layers
Config Service (Port 8888)
|
+----------+-----------+-----------+-----------+
| | | | |
REST API WebSocket Kafka Health Actuator
(12 ctrl) (streaming) (events) (/health) (/prometheus)
| | | | |
+----------+-----------+-----------+-----------+
|
+------------+------------+
| | |
Service Layer Cache Layer Audit Layer
(13 services) (Redis) (change tracking)
| | |
+------------+------------+
|
+------------+------------+
| | |
JPA Repos Redis Repos Kafka Producer
(14 repos) (cache) (change events)
| | |
+------------+------------+
|
PostgreSQLControllers
The Config Service exposes 12 REST controllers, each handling a distinct area of configuration management:
| Controller | Base Path | Purpose |
|---|---|---|
ConfigController | /api/v1/configs | Configuration entry CRUD, versioning, rollback |
FeatureFlagController | /api/v1/feature-flags | Feature flag lifecycle, evaluation, rollout |
ABTestingController | /api/v1/experiments | A/B testing experiments, variant assignment |
TargetingController | /api/v1/targeting | Targeting rule evaluation engine |
SecretController | /api/v1/config/secrets | Encrypted secret management |
ApprovalWorkflowController | /api/v1/config/approvals | Config change approval workflows |
EnvironmentPromotionController | /api/v1/config/promotions | Cross-environment config promotion |
BulkImportExportController | /api/v1/config/bulk | Bulk import/export operations |
FlagSchedulingController | /api/v1/schedules | Time-based flag scheduling |
ConfigAuditController | /api/v1/config/audit | Configuration change audit trail |
CacheController | /api/v1/cache | Cache statistics and invalidation |
HealthController | /health | Health and readiness probes |
Configuration Hierarchy
Configurations resolve through a four-level hierarchy. Each level overrides the previous:
| Level | Scope | Example | Source |
|---|---|---|---|
| Global | Platform-wide defaults | query.timeout=30s | Database (null tenantId) |
| Environment | Per-environment | query.timeout=60s (production) | Database (environment field) |
| Tenant | Per-tenant customization | query.timeout=120s | Database (tenantId set) |
| Override | Emergency overrides | query.timeout=10s | Redis cache (TTL-based) |
Resolution Flow
// ConfigService.getConfigWithHierarchy()
// 1. Check tenant+environment specific config
// 2. Fall back to tenant-level config
// 3. Fall back to environment-level config
// 4. Fall back to global config
public Optional<ConfigEntry> getConfigWithHierarchy(
UUID tenantId, String configKey, String environment) {
// Tenant + environment specific
Optional<ConfigEntry> tenantEnv = repository.findByTenantIdAndConfigKeyAndEnvironment(
tenantId, configKey, environment);
if (tenantEnv.isPresent()) return tenantEnv;
// Tenant level
Optional<ConfigEntry> tenant = repository.findByTenantIdAndConfigKey(tenantId, configKey);
if (tenant.isPresent()) return tenant;
// Global
return repository.findByTenantIdIsNullAndConfigKey(configKey);
}Entity Model
ConfigEntry
The core entity for all configuration entries:
@Entity
@Table(name = "config_entries")
public class ConfigEntry {
@Id
private UUID id;
private UUID tenantId; // null for global configs
private String configKey; // e.g., "query.timeout"
private String configValue; // the value
private String category; // grouping category
private String description;
private String environment; // dev, staging, production
private String valueType; // STRING, INTEGER, BOOLEAN, JSON
private boolean encrypted; // whether value is encrypted
private Integer version; // auto-incrementing version
private UUID createdBy;
private Instant createdAt;
private Instant updatedAt;
}FeatureFlag
@Entity
@Table(name = "feature_flags")
public class FeatureFlag {
@Id
private UUID id;
private UUID tenantId;
private String flagKey;
private String displayName;
private String description;
private String flagType; // BOOLEAN, STRING, NUMBER, JSON
private boolean enabled;
private String defaultValue;
private Integer rolloutPercentage;
private List<UUID> allowedUsers;
private List<Map<String, Object>> rules; // targeting rules (JSON)
private Set<String> tags;
private Instant createdAt;
private Instant updatedAt;
}Event-Driven Updates
When configuration values change, the Config Service publishes events to Kafka:
Kafka Topics
| Topic | Description |
|---|---|
config.changes | All configuration entry changes |
config.feature-flags | Feature flag toggle and update events |
config.secrets | Secret rotation and access events |
config.promotions | Environment promotion events |
Change Event Schema
{
"eventType": "CONFIG_UPDATED",
"configId": "550e8400-e29b-41d4-a716-446655440000",
"tenantId": "660e8400-e29b-41d4-a716-446655440000",
"configKey": "query.timeout",
"oldValue": "60s",
"newValue": "120s",
"changedBy": "admin@acme.com",
"environment": "production",
"timestamp": "2026-02-12T10:00:00Z",
"correlationId": "req-abc-123"
}Security Model
All endpoints require JWT authentication. The service extracts identity from headers:
| Header | Purpose | Required |
|---|---|---|
X-Tenant-ID | Tenant scoping for multi-tenancy | Most endpoints |
X-User-ID | Actor identification for audit | Write operations |
X-User-Email | Human-readable actor identity | Write operations |
X-User-Role | Role-based access control | Approval workflows |
Authorization | Bearer JWT token | All endpoints |
Secret endpoints additionally require X-Service-Name for service-level access control.
Configuration
Application Properties
server:
port: 8888
spring:
datasource:
url: jdbc:postgresql://${DB_HOST}:5432/matih_config
username: ${DB_USERNAME}
password: ${DB_PASSWORD}
redis:
host: ${REDIS_HOST}
port: 6379
kafka:
bootstrap-servers: ${KAFKA_BOOTSTRAP_SERVERS}
config-service:
encryption:
algorithm: AES/GCM/NoPadding
key-length: 256
cache:
ttl: 300s
max-size: 10000
scheduling:
poll-interval: 30sHealth and Metrics
| Endpoint | Description |
|---|---|
GET /health | Liveness probe |
GET /health/ready | Readiness (DB + Redis + Kafka) |
GET /actuator/prometheus | Prometheus metrics |
Key Metrics
| Metric | Type | Description |
|---|---|---|
config_requests_total | Counter | Total configuration requests |
config_cache_hit_ratio | Gauge | Redis cache hit rate |
config_changes_total | Counter | Configuration changes by type |
feature_flag_evaluations_total | Counter | Flag evaluations by flag key |
secret_access_total | Counter | Secret access events |
ab_experiment_assignments_total | Counter | Experiment variant assignments |
Next Steps
- Configuration Management -- CRUD operations, versioning, and rollback
- Feature Flags -- flag lifecycle and evaluation
- A/B Testing -- experiment management
- Secret Management -- encrypted secrets
- API Reference -- complete endpoint listing