MATIH Platform is in active MVP development. Documentation reflects current implementation status.
8. Platform Services
Architecture

Config Service Architecture

Production - 12 controllers, full feature flag system

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

PropertyValue
Service Nameconfig-service
Port8888
TechnologySpring Boot 3.2, Java 21, Spring Cloud Config
DatabasePostgreSQL (JPA/Hibernate)
CacheRedis (distributed config cache)
Event BusKafka (config change events)
LockingRedis distributed locks
API DocumentationOpenAPI 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)
              |            |            |
              +------------+------------+
                           |
                      PostgreSQL

Controllers

The Config Service exposes 12 REST controllers, each handling a distinct area of configuration management:

ControllerBase PathPurpose
ConfigController/api/v1/configsConfiguration entry CRUD, versioning, rollback
FeatureFlagController/api/v1/feature-flagsFeature flag lifecycle, evaluation, rollout
ABTestingController/api/v1/experimentsA/B testing experiments, variant assignment
TargetingController/api/v1/targetingTargeting rule evaluation engine
SecretController/api/v1/config/secretsEncrypted secret management
ApprovalWorkflowController/api/v1/config/approvalsConfig change approval workflows
EnvironmentPromotionController/api/v1/config/promotionsCross-environment config promotion
BulkImportExportController/api/v1/config/bulkBulk import/export operations
FlagSchedulingController/api/v1/schedulesTime-based flag scheduling
ConfigAuditController/api/v1/config/auditConfiguration change audit trail
CacheController/api/v1/cacheCache statistics and invalidation
HealthController/healthHealth and readiness probes

Configuration Hierarchy

Configurations resolve through a four-level hierarchy. Each level overrides the previous:

LevelScopeExampleSource
GlobalPlatform-wide defaultsquery.timeout=30sDatabase (null tenantId)
EnvironmentPer-environmentquery.timeout=60s (production)Database (environment field)
TenantPer-tenant customizationquery.timeout=120sDatabase (tenantId set)
OverrideEmergency overridesquery.timeout=10sRedis 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

TopicDescription
config.changesAll configuration entry changes
config.feature-flagsFeature flag toggle and update events
config.secretsSecret rotation and access events
config.promotionsEnvironment 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:

HeaderPurposeRequired
X-Tenant-IDTenant scoping for multi-tenancyMost endpoints
X-User-IDActor identification for auditWrite operations
X-User-EmailHuman-readable actor identityWrite operations
X-User-RoleRole-based access controlApproval workflows
AuthorizationBearer JWT tokenAll 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: 30s

Health and Metrics

EndpointDescription
GET /healthLiveness probe
GET /health/readyReadiness (DB + Redis + Kafka)
GET /actuator/prometheusPrometheus metrics

Key Metrics

MetricTypeDescription
config_requests_totalCounterTotal configuration requests
config_cache_hit_ratioGaugeRedis cache hit rate
config_changes_totalCounterConfiguration changes by type
feature_flag_evaluations_totalCounterFlag evaluations by flag key
secret_access_totalCounterSecret access events
ab_experiment_assignments_totalCounterExperiment variant assignments

Next Steps