The Feature Flag Evaluator is a modern, high-performance rules engine designed to determine whether specific feature flags are enabled or disabled for given target audiences. Built with Spring Boot 3.5.x and leveraging Camunda Platform 7.24.0, the system provides two distinct pathways: a sub-millisecond, low-latency API evaluation mechanism for real-time application requests, and an interactive BPMN workflow simulation path for workflow modeling, compliance, operator audit, and human-in-the-loop operational checks.
The application is structured into clearly bounded layers. Communication between the user browser, the Spring core container, the Camunda embedded workflow engine, and the persistent storage database is mapped in the diagram below:
graph TB
subgraph Client Layer
Browser[Web Browser / API Client]
Modeler[Camunda Modeler]
end
subgraph Flag Evaluator Spring Boot Application Boundary
subgraph REST API Controller Layer
FC[FlagController]
JerseyREST[Camunda Jersey REST Servlet]
end
subgraph Spring Managed Business Context
RulesService[RulesService <br> - Reads YAML Feature Config <br> - Implements Rule Evaluation]
Cache[FeatureDetailService <br> - Concurrent In-Memory Cache <br> - Thread-Safe Map]
Jackson[JacksonConfig <br> - Custom Serialization Mapping]
end
subgraph Embedded Camunda 7.24 Engine Layer
Runtime[RuntimeService <br> - Starts process instances]
TaskService[TaskService <br> - Manages User Tasks & Claims]
JobExec[SpringJobExecutor <br> - Handles asyncBefore/asyncAfter background jobs <br> - Thread Pool: 3-10 workers]
Deployer[BPMN Resource Deployer <br> - Deploys classpath:processes/*.bpmn]
end
subgraph Data & Persistence Infrastructure Layer
Hikari[HikariDataSource <br> - Connection Pool]
JPA[Hibernate JPA / Hibernate ORM]
end
end
subgraph Database Storage Layer
H2[(H2 Local File DB <br> camunda-h2-database)]
end
%% Network & Interface Protocols
Browser -->|HTTP REST / JSON / api/eval| FC
Browser -->|HTTP REST / JSON / api/executetask| FC
Browser -->|HTTP REST / HTML / Web Console| JerseyREST
Modeler -->|BPMN 2.0 XML File Write| Deployer
%% Engine & Service Wiring
FC -->|Java API: Query Cache| Cache
FC -->|Java API: Trigger Process| Runtime
Runtime -->|Service Delegation / JavaDelegate| RulesService
RulesService -->|Cache Writes| Cache
TaskService -->|Context Variables Read/Write| Hikari
JobExec -->|Async Job Thread| Runtime
%% Storage connections
Hikari -->|JDBC Connections| H2
JPA -->|ORM Transactions| Hikari
Deployer -->|Saves Deployed XML Definitions| JPA
Runtime -->|Persists Token State| JPA
The backend is built as a lightweight, reactive-friendly monolithic Spring Boot application, optimized for execution under Java 25. It incorporates modern language paradigms such as Module Import Declarations (import module java.base;) and Java Records to achieve optimal type safety and readable, concise code structures.
- Spring Boot Version: 3.5.14
- Camunda Platform Version: 7.24.0 (Spring Boot Starter)
- Java Virtual Machine: OpenJDK / GraalVM (Java 25 compatible)
- Database Platform: Embedded H2 Database (File-backed:
jdbc:h2:file:./camunda-h2-database) - ORM & JPA Provider: Hibernate ORM 6.6.x
net.ironoc.rules.engine
├── ApiApplication.java # Main Application Bootstrapper
├── config
│ └── JacksonConfig.java # Customized JSON Serializers & ObjectMapper
├── controller
│ └── FlagController.java # Inbound REST API Endpoint Handler
├── service
│ ├── DetailCacheI.java # In-Memory Cache Interface
│ ├── FeatureDetailService.java # Concurrent In-Memory Cache Implementation
│ ├── RuleServiceI.java # Core Rules Engine Evaluation Interface
│ └── RulesService.java # Bind Configuration & Matches Rules Logic
├── dto
│ ├── Feature.java # Feature Flag DTO Record
│ ├── RuleGroups.java # All & Any Rules Record
│ └── Rule.java # Individual Rule Condition Record
└── enums
├── FeatureFlag.java # Enumerated Evaluated Attributes (TIER, APPVERSION, COUNTRY)
├── RuleOperator.java # Evaluation Operators (IN, EQ, GTE, GT)
├── RuleGroup.java # Logic Groups (ALL, ANY)
└── Country.java # Region Enums
Exposes three distinct endpoints:
GET /api/test: Health verification endpoint that maps the standard supported countries (Countryenum).GET /api/executetask: Manually fires up aRules_matcherCamunda process instance. It registers current request inputs, loads initial rule sets into the execution context, and returns the uniqueprocessInstanceId.GET /api/eval: The high-performance direct flag evaluation path. It consumes request parameters (feature,country,appVersion,tier), checks the configuration inFeatureDetailService, executes the matching algorithm, and returns 200 OK with the array of matched rules, or a 400 Bad Request with an empty list if rules do not pass.
Implements DetailCacheI using a ConcurrentHashMap. To eliminate slow external configurations or DB reads during flag evaluation, this cache holds pre-compiled feature and rule records in memory.
The brain of the evaluation system. It handles two jobs:
- Config Binding (
executemethod): Programmatically binds the prefixfeatureconfigurations inapplication.ymldirectly into rich Java record representations (Feature,RuleGroups,Rule) using Spring'sBinderAPI. - Rules Evaluation (
rulesMatchermethod): Matches incoming client parameters against target rule attributes (FeatureFlagattributes) utilizing targeted criteria operators. Supported operations include:IN&EQ: Validates exact string containment/equality (e.g., verifying a country is in[ES, PT]or the user's tier matchesgold).GTE>: Evaluates numerical strings (e.g., validating the user's appVersion is greater than or equal to120).
By relying on Java 16+ records (Feature, RuleGroups, Rule), the application guarantees structural immutability, thread safety, and standard serialization behaviors with minimal boilerplate.
The system supports automatic bootstrapping of rules on system boot, alongside multiple operational pathways for manual execution, reloads, and configurations.
┌────────────────────────────────────────────────────────────────────────────────────────────┐
│ BOOTSTRAPPING & TRIGGERING PATHWAYS │
│ │
│ [Startup Lifecycle] │
│ │ │
│ ▼ │
│ (PostDeployEvent) ──> [Auto-Trigger Rules_matcher Process] ──> [Memory Cache Loaded] │
│ │
│ │
│ [Operational Inputs] │
│ │ │
│ ├─► (REST Endpoint GET /api/executetask) ────────┐ │
│ │ ├─► [Trigger Rules_matcher] │
│ ├─► (Camunda REST API: /process-definition/...) ─┘ │
│ │ │
│ └─► (Camunda Tasklist UI) ─────────────────────────► [Interactively Run Process] │
└────────────────────────────────────────────────────────────────────────────────────────────┘
To ensure that rule evaluations are ready for immediate consumption upon application launch, the system implements an automated deployment-event listener:
- Camunda Deployment Completion: As the Spring Boot container starts, the embedded Camunda Process Engine loads, registers, and deploys the BPMN files found inside
src/main/resources/processes/(such asrules-matcher.bpmn). - Post-Deployment Lifecycle Interception: Under
ApiApplication.java, a Spring-managed event listener intercepts the deployment phase using the@EventListenerannotation bound to Camunda'sPostDeployEvent:@EventListener public void processPostDeploy(PostDeployEvent event) { runtimeService.startProcessInstanceByKey("Rules_matcher"); }
- Execution Kick-off: Immediately upon receiving the
PostDeployEvent, Camunda starts an instance of theRules_matcherprocess in the background. - Cache Initialization: The newly created process instance immediately moves to its first executable node: Service Task
rules-init(linked toRulesService). This service task executesRulesService.execute(...), programmatically binds the rules prefixfeatureconfigurations directly fromapplication.yml, and populates theFeatureDetailServicein-memory concurrency cache. - Interactive Pause State: The process then advances to the User Task
Activity_UserConfirmInit("Review rules-init output"), parking a token there for manual operator validation in the Camunda Tasklist, concluding the startup bootstrap safely.
When configurations change in real-time, or operators need to run manual verification loops, they can trigger rule loading through three pathways:
Designed for testing, programmatic refreshes, and quick re-trigger loops:
- Calling
http://localhost:8080/api/executetasktargets theFlagController. - The controller leverages Camunda's
RuntimeServiceto start a new instance of the process key"Rules_matcher"and retrieves process variables synchronously upon execution:ProcessInstanceWithVariables result = runtimeService .createProcessInstanceByKey("Rules_matcher") .executeWithVariablesInReturn();
- This triggers a clean execution of the
rules-initdelegate, rebuilding the concurrent map cache with updated environment values, and registers a new tracking token in the engine.
Enterprise orchestration platforms or external CD pipelines can manually trigger a rule execution flow using Camunda’s native REST interface:
- HTTP Endpoint:
POST http://localhost:8080/engine-rest/process-definition/key/Rules_matcher/start - Content-Type:
application/json - Payload Structure: Allows variables to be loaded directly into execution memory for testing:
{ "variables": { "feature": { "value": "new-checkout", "type": "String" }, "country": { "value": "ES", "type": "String" }, "appVersion": { "value": "130", "type": "String" }, "tier": { "value": "gold", "type": "String" } } }
Operators can visually trigger manual execution flows with custom, interactive values:
- Log into the Camunda Tasklist portal at
http://localhost:8080/app/tasklist/. - Click "Start process" in the top navigation panel.
- Select "Rules Matcher Workflow (In Progress)" from the list of deployed definitions.
- (Optional) Provide start variables directly in the generic process starter modal.
- Click "Start" to visually run the workflow token, inspect user tasks, complete review blocks, and trace matches interactively.
When manual testing or operations personnel execute, interact with, or audit the Rules_matcher workflow process, they navigate through a structured graphical interface. This end-to-end user experience flow across the Camunda Tasklist and Cockpit dashboards is detailed in the state chart below:
stateDiagram-v2
[*] --> Operator_Login : Access http://localhost:8080/
state Operator_Login {
[*] --> Enter_Credentials : sa / passw
Enter_Credentials --> Dashboard_Redirect : Click Login
}
Dashboard_Redirect --> Camunda_Tasklist : Select Tasklist App
Dashboard_Redirect --> Camunda_Cockpit : Select Cockpit App
state Camunda_Tasklist {
[*] --> Start_Process_Modal : Click "Start process"
Start_Process_Modal --> Select_Rules_Matcher : Select "Rules Matcher Workflow"
Select_Rules_Matcher --> Input_Start_Variables : (Optional) Input "feature", "country", etc.
Input_Start_Variables --> Process_Started : Click "Start"
state Task_Lifecycle_Phase_1 {
Process_Started --> Fetch_Pending_Tasks : Apply "All tasks" Filter
Fetch_Pending_Tasks --> Claim_Init_Task : Select "Review rules-init output"
Claim_Init_Task --> View_Feature_Details : Inspect Process Variables
View_Feature_Details --> Complete_Init_Task : Click "Complete"
}
state Task_Lifecycle_Phase_2 {
Complete_Init_Task --> Token_Routing_In_Engine : (System routes via Gateways & Delegates)
Token_Routing_In_Engine --> Fetch_New_Review_Task : Poll / Refresh list
Fetch_New_Review_Task --> Claim_Review_Task : Select "Review matched rules JSON"
Claim_Review_Task --> Inspect_rulesJson : View matched rules in serialized output
Inspect_rulesJson --> Complete_Review_Task : Click "Complete"
}
Complete_Review_Task --> Process_Completed : Process Instance terminates
}
state Camunda_Cockpit {
[*] --> Navigate_Process_Definitions : Click "Processes"
Navigate_Process_Definitions --> Select_Rules_Matcher_Def : Click "Rules_matcher"
Select_Rules_Matcher_Def --> Inspect_Live_Tokens : View heat map / token locations
Inspect_Live_Tokens --> Inspect_Variables : View runtime process variable values
Inspect_Live_Tokens --> Inspect_Historic_Audit : Track completed delegates & paths taken
}
The system relies on executable Business Process Model and Notation (BPMN 2.0) specifications deployed to the embedded Camunda engine.
The application deploys three separate workflows on boot:
rules-init(Process IDrules-initviafirst.bpmn): Fully automated startup helper process. Triggered automatically or via internal hooks, it executes a service task linked directly toRulesService.execute(...)to read from physical files (application.yml), perform binding, and load target configurations directly into the concurrent cache map.Rules_matcher(Process IDRules_matcherviarules-matcher.bpmn): The comprehensive execution workflow implementing conditional logical routing, custom java delegate evaluations, async state persistence boundaries, and interactive user checkpoint nodes.loanApproval(Process IDloanApprovalvialoanApproval.bpmn): A lightweight demo user-task assignment workflow.
The Rules_matcher workflow incorporates specific structures to govern execution based on live evaluation states:
graph TD
Start([Start Event]) --> Init[rules-init <br> RulesService]
Init --> UserConfirm[User Task: Review rules-init output <br> Pause for Tasklist]
UserConfirm --> CheckEnabled[Enabled <br> FeatureEnabledDelegate]
CheckEnabled --> Gateway{Feature Enabled?}
%% Enabled branch
Gateway -->|Yes / featureEnabled| AppVer[Application Version Supported? <br> AppVersionDelegate]
AppVer --> Tier[Tier Valid for User? <br> TierDelegate]
Tier --> Country[Country Supported? <br> CountryDelegate]
Country --> Merge[Gateway: MergeBeforeAggregator]
%% Disabled branch
Gateway -->|No / !featureEnabled| Disabled[Disabled <br> FeatureDisabledDelegate]
Disabled --> Pass[Empty Rule Set <br> PassEngineDelegate]
Pass --> Merge
Merge --> Aggregator[Return Rules Set <br> RulesAggregatorDelegate]
Aggregator --> UserReview[User Task: Review matched rules JSON <br> Pause for Tasklist]
UserReview --> End([End Event])
- Routing Gate (
Gateway_0ejhppi): Acts as a deterministic fork. It evaluates the process variable${featureEnabled}populated byFeatureEnabledDelegate. Iftrue, it diverts the token down the evaluation path. Iffalse, it redirects the token to the cleanup/disabled path. - Merging Gate (
Gateway_MergeBeforeAggregator): Serves as an un-synchronized convergence node. Whether the process resolved rules or skipped them entirely, both paths converge at this merge node prior to triggering rule aggregation.
BPMN user tasks represent operational safety checkpoints:
Activity_UserConfirmInit("Review rules-init output"): Positioned immediately after rule loading. The process stops and presents the task in the Camunda Tasklist. This ensures that operators verify that features and rule definitions are properly loaded from YAML configurations before actual criteria matching starts.Activity_UserReviewRulesJson("Review matched rules JSON"): Positioned immediately after aggregate evaluation. This blocks completion until an operator claims and completes the task in the Camunda Tasklist. This acts as a manual audit boundary to inspect the serializedrulesJsonresult.
The Service Task Activity_1pygerh ("Return Rules Set") is configured with camunda:asyncBefore="true" and camunda:asyncAfter="true":
asyncBefore=true: Before entering the delegate, the engine commits the current database transaction. The execution thread is released back to the caller (e.g. the HTTP request), and a background job is scheduled. The Camunda Job Executor picks up the task and runRulesAggregatorDelegatein a background worker thread.asyncAfter=true: Immediately after the delegate completes its work, the engine commits the state variables and saves the updated matched rules back to the database, ensuring zero data loss before transitioning to the subsequent user task checkpoint.
Each service node in the Rules_matcher process is backed by a specific Java class implementing org.camunda.bpm.engine.delegate.JavaDelegate. These classes orchestrate process state transitions by reading, updating, and removing execution variables.
Below is an exhaustive account of each delegate's responsibilities, input/output variables, and internal execution logic:
┌────────────────────────────────────────────────────────────────────────────────────────────┐
│ RULES_MATCHER PROCESS │
│ │
│ [Start] ──> [rules-init] │
│ │ │
│ ▼ │
│ (Task: Review rules-init) │
│ │ │
│ ▼ │
│ [FeatureEnabled] ─────────────────────────────────────────┐ │
│ │ │ │
│ (featureEnabled == true) (featureEnabled == false) │
│ │ │ │
│ ▼ ▼ │
│ [AppVersion] [FeatureDisabled] │
│ │ │ │
│ ▼ ▼ │
│ [Tier] [PassEngine] │
│ │ │ │
│ ▼ │ │
│ [Country] │ │
│ │ │ │
│ └─────────────────────────► ◄──────────────────────┘ │
│ │ │
│ ▼ │
│ [RulesAggregator] │
│ │ │
│ ▼ │
│ (Task: Review rules JSON) │
│ │ │
│ ▼ │
│ [End] │
└────────────────────────────────────────────────────────────────────────────────────────────┘
- Purpose: Acts as the primary context initializer. It queries the cache to check if the target feature is enabled and loads rule sets into the active process execution scope.
- Class Path:
net.ironoc.rules.engine.delegate.FeatureEnabledDelegate - State Transitions:
- Inputs (Read):
feature(String): The ID of the feature flag to evaluate.
- Outputs (Written):
featureEnabled(Boolean): Flag representing whether the requested feature is active.featureDto(Feature - Java Record): The fully populated Feature object containing logical groups.ruleGroupsAll(Map<String, Map<String, Object>>): Rule sets that must pass AND conditions (mapped fromfeature.ruleGroups().all()).ruleGroupsAny(Map<String, Map<String, Object>>): Rule sets that must pass OR conditions (mapped fromfeature.ruleGroups().any()).
- Scope Removals (On Feature Disabled/Missing):
- Removes variables
featureDto,ruleGroupsAll, andruleGroupsAnyfrom the execution context to prevent stale configuration pollution.
- Removes variables
- Inputs (Read):
- Detailed Steps:
- Retrieves the string value of the process variable
"feature". If null, defaults to empty. - Queries the concurrent cache (
featureDetailsService.getFeaturesById()) using the feature ID. - Evaluates if the feature is non-null and
feature.enabled()is true. - Calls
execution.setVariable("featureEnabled", enabled). - If
enabledistrue, extracts the underlying logicalruleGroupsconfigurations and registers them as serialized process variables ("featureDto","ruleGroupsAll","ruleGroupsAny") so downstream delegates can access them. - If
disabledormissing, callsexecution.removeVariable(...)for all feature-specific parameters and logs the cleanup.
- Retrieves the string value of the process variable
- Purpose: Forces a process-level override to disable the active feature context. Used exclusively in the "Disabled" branch of the gateway.
- Class Path:
net.ironoc.rules.engine.delegate.FeatureDisabledDelegate - State Transitions:
- Inputs (Read): None.
- Outputs (Written):
featureEnabled(Boolean): Overridden tofalse.
- Scope Removals:
- Clears variables
featureDto,ruleGroupsAll, andruleGroupsAnyfrom the execution scope.
- Clears variables
- Detailed Steps:
- Calls
execution.setVariable("featureEnabled", false)to ensure that any conflicting upstream evaluation is overridden. - Removes variables
"featureDto","ruleGroupsAll", and"ruleGroupsAny"to prevent evaluation. - Logs the cleanup sequence.
- Calls
- Purpose: Bypasses evaluation steps when a feature is disabled, short-circuiting rule execution.
- Class Path:
net.ironoc.rules.engine.delegate.PassEngineDelegate - State Transitions:
- Inputs (Read): None.
- Outputs (Written):
skipRulesEngine(Boolean): Set totrue.matchedRules(List): Initialized to an emptyArrayList<Rule>().
- Detailed Steps:
- Sets process variable
"skipRulesEngine"totrueto signal downstream aggregators that rule matching should be skipped. - Instantiates an empty
ArrayList<Rule>and binds it to the process variable"matchedRules". - Logs the bypass transition.
- Sets process variable
- Purpose: Extracts, normalizes, and captures client-supplied application version parameters.
- Class Path:
net.ironoc.rules.engine.delegate.AppVersionDelegate - State Transitions:
- Inputs (Read):
appVersion(String): Raw client-submitted application version.
- Outputs (Written): None (captures and sanitizes parameters; actual matching executes in the aggregator).
- Inputs (Read):
- Detailed Steps:
- Retrieves the execution variable
"appVersion". - Converts the value to a string, trims whitespace, and defaults to empty if null.
- Logs the captured parameter for trace visibility.
- Retrieves the execution variable
- Purpose: Extracts, normalizes, and captures client-supplied tier parameters.
- Class Path:
net.ironoc.rules.engine.delegate.TierDelegate - State Transitions:
- Inputs (Read):
tier(String): Raw client-submitted subscription tier.
- Outputs (Written): None.
- Inputs (Read):
- Detailed Steps:
- Retrieves the execution variable
"tier". - Normalizes, trims, and defaults the value.
- Logs the normalized value.
- Retrieves the execution variable
- Purpose: Extracts, normalizes, and captures client-supplied country parameters.
- Class Path:
net.ironoc.rules.engine.delegate.CountryDelegate - State Transitions:
- Inputs (Read):
country(String): Raw client-submitted country code.
- Outputs (Written): None.
- Inputs (Read):
- Detailed Steps:
- Retrieves the execution variable
"country". - Normalizes, trims, and defaults the value.
- Logs the normalized value.
- Retrieves the execution variable
- Purpose: Aggregates and evaluates rule matching across different groups (AND / OR) and serializes results into an audited process string.
- Class Path:
net.ironoc.rules.engine.delegate.RulesAggregatorDelegate - State Transitions:
- Inputs (Read):
skipRulesEngine(Boolean): Check to skip criteria matching.featureEnabled(Boolean): Check if feature is active.country(String): Client's country code.appVersion(String): Client's application version.tier(String): Client's subscription tier.feature(String): Target feature ID.
- Outputs (Written):
matchedRules(List): List of matched rule records.rulesJson(String): JSON serialized string containing matching rules, rendered directly in Camunda Tasklist.
- Inputs (Read):
- Detailed Steps:
- Evaluates process variables
"skipRulesEngine"and"featureEnabled". - Short-Circuit Evaluation: If
skipRulesEngineistrueorfeatureEnabledisfalse, setsmatchedRulesto an emptyArrayList<Rule>(). - Full Match Evaluation:
- Retrieves client arguments:
country,appVersion,tier, andfeatureId. - Retrives the feature details from cache.
- Invokes
rulesService.getRuleMatchByRuleGroup(...)for logical groupALL(representing criteria that must all match / AND). - Invokes
rulesService.getRuleMatchByRuleGroup(...)for logical groupANY(representing criteria where at least one must match / OR). - Compiles rules from both groups using
rulesService.createResponseFromMatches(...).
- Retrieves client arguments:
- Binds the resulting list of matches to the process variable
"matchedRules". - Serializes the match collection to a JSON string using Jackson
objectMapper. - Saves the JSON string as the process variable
"rulesJson". - Logs execution metrics (e.g., number of aggregated rules matched).
- Evaluates process variables
The integration of Camunda provides a robust framework to visualize, monitor, audit, and walk through business rules interactively.
Architects, product owners, and developers use the Camunda Modeler to edit .bpmn files. Modeler features utilized in this codebase include:
- Service Tasks: Executed automatically by referencing Spring Beans or Java Delegates (e.g.,
camunda:class="net.ironoc.rules.engine.delegate.FeatureEnabledDelegate"). - Exclusive Gateways: Controls routing based on expression variables (e.g., evaluating
${featureEnabled}vs${!featureEnabled}). - User Tasks: Introduces deliberate pause points (such as
Review rules-init outputorReview matched rules JSON) to allow administrators to examine intermediate results via the web UI. - Asynchronous Continuations: Configured on key tasks via
camunda:asyncBefore="true"orcamunda:asyncAfter="true". This instructs the engine to commit the current transaction to the database, allowing background job executors to handle execution, preventing long-running operations from blocking HTTP request threads.
Upon booting the application, the Camunda Platform Cockpit, Tasklist, and Admin interfaces are hosted at http://localhost:8080/ (Admin credentials default to: sa / passw).
Provides an overview of running processes. Operators use it to:
- View active process instances and pinpoint exactly which task token is currently executing.
- Analyze historical executions, audit paths taken, and trace variables.
- Perform runtime interventions (e.g., re-run a failed delegate step or manually edit process variables like
countryorappVersionduring live execution).
The interactive interface for operations teams. Because the Rules_matcher workflow incorporates User Tasks, executing a workflow creates a task entry here.
- Review rules-init output: The operator inspects the loaded feature configuration from yaml before evaluating rules.
- Review matched rules JSON: The operator views the aggregated matches stored in
rulesJsonbefore completing the process. - Operators claim, view, update values, and complete tasks to push the process to the next step.
Controls user authentication, authorizations, and filter creation (e.g., configures the "All tasks" filter used to display pending user tasks in the Tasklist).
The Feature Flag Evaluator supports two distinct execution paths depending on performance and audit requirements:
Designed for live production traffic requiring sub-millisecond responses.
sequenceDiagram
autonumber
actor Client as API Caller
participant Controller as FlagController
participant Cache as FeatureDetailService (In-Memory Cache)
participant Service as RulesService
Client->>Controller: GET /api/eval (feature, country, appVersion, tier)
Controller->>Cache: getFeaturesById()
Cache-->>Controller: Feature DTO (enabled, ruleGroups)
alt Feature not found or disabled
Controller-->>Client: 400 Bad Request (empty list)
else Feature is enabled and has ruleGroups
Controller->>Service: getRuleMatchByRuleGroup(RuleGroup.ALL)
Service->>Service: Evaluate GTE, GT, IN, EQ conditions
Service-->>Controller: allRuleMatch List
Controller->>Service: getRuleMatchByRuleGroup(RuleGroup.ANY)
Service->>Service: Evaluate GTE, GT, IN, EQ conditions
Service-->>Controller: anyRuleMatch List
Controller->>Service: createResponseFromMatches(allRuleMatch, anyRuleMatch)
Service-->>Controller: ResponseEntity<ApiResponse>
alt Matches found
Controller-->>Client: 200 OK (ApiResponse with matched Rules JSON)
else No matches
Controller-->>Client: 400 Bad Request (empty list)
end
end
Designed for process tracing, visual auditing, and manual user checkpoints.
- Triggering: The client calls
GET /api/executetask. TheFlagControllerstarts theRules_matcherBPMN process via the Camunda Runtime Service. - Rule Binding (
rules-initService Task): ExecutesRulesService. It binds rule specifications fromapplication.ymland updatesFeatureDetailService. - Manual Gateway Pause (
Review rules-init outputUser Task): The process pauses. The operator claims and completes the task in Camunda Tasklist. - Context Evaluation (
EnabledService Task): RunsFeatureEnabledDelegate. It extracts the process variablefeature. It checks the cache, setsfeatureEnabledtotrueorfalse, and pushes rule configurations into process variables (ruleGroupsAll,ruleGroupsAny). - Gateway Routing (
Feature Enabled?Exclusive Gateway):- If Enabled: Routes to evaluation delegates:
AppVersionDelegate: Captures and logsappVersion.TierDelegate: Captures and logstier.CountryDelegate: Captures and logscountry.
- If Disabled: Routes to teardown delegates:
FeatureDisabledDelegate: Formally overridesfeatureEnabledto false and clears remaining cache variables.PassEngineDelegate: Signals that rules should be skipped (skipRulesEngine=true) and initializes an empty matched rules array.
- If Enabled: Routes to evaluation delegates:
- Merging & Aggregation (
Return Rules SetService Task): Merges both flows and routes toRulesAggregatorDelegate.- If rules are skipped or the feature is disabled, it constructs an empty match list.
- Otherwise, it reads the input variables (
country,appVersion,tier,feature), usesRulesServiceto match criteria against rule attributes, aggregates all valid rules, saves them tomatchedRules, and serializes the list to a process variable stringrulesJson.
- Final Review Pause (
Review matched rules JSONUser Task): Pauses the process. An operator reviews the compiled rules inrulesJsoninside the Tasklist. Once approved, the task is marked complete, and the instance finishes.
The system guarantees robust operations via its comprehensive, self-contained test suite containing 9 distinct unit and integration tests distributed across these target segments:
JacksonConfigTest: Validates customized JSON serialization configurations, ensuring records and complex maps serialize smoothly.ContextLoadsTest: Boots up the full Spring Application Context, verifies Hibernate mappings, initializes the Hikari Connection Pool to the local H2 file database, and checks that Camunda BPMN processes (rules-matcher.bpmn,first.bpmn,loanApproval.bpmn) deploy cleanly.RulesServiceTest(Unit Tests): Tests the parsing engine logic in isolation. It verifies:- Logical country string inclusions (
IN). - Numerical application version comparisons (
GTE,GT). - Safe handling of unsupported criteria operators.
- Logical country string inclusions (
FlagControllerTest(Controller Mock Tests): Exercises flag-evaluation REST endpoints directly, validating appropriate HTTP response codes (200 OK vs 400 Bad Request) for diverse execution scenarios (e.g., missing features, disabled flags, or composite rule criteria matches across both ALL and ANY groups).
This comprehensive architecture maintains highly performant runtime capabilities alongside rigorous operational oversight, meeting both enterprise-grade API performance demands and corporate compliance goals.