This file provides comprehensive AI-specific guidance for Claude Code when working with the library-api codebase.
For human-friendly quick start, see README.md. This file contains detailed architecture, troubleshooting, and AI assistant context.
Core concept: DMN files in src/main/resources/ automatically generate REST endpoints at POST /api/v1/{path-to-dmn}
Start dev server: bin/dev (serves at http://localhost:8083)
Critical constraint: Every DMN model must have a Decision Service named {ModelName}Service to be exposed via API.
-
Always validate naming conventions:
- Model names must be unique across all DMN files
- Decision Service must be named
{ModelName}Service(e.g., "PersonMinAge" → "PersonMinAgeService") - File names should be kebab-case (e.g.,
person-min-age.dmn)
-
Import hierarchy is critical:
- BDT.dmn is the root (defines tSituation and shared types)
- Checks import BDT.dmn and relevant base modules (Age.dmn, Enrollment.dmn)
- Benefits import BDT.dmn, Benefits.dmn, and needed check DMNs
- All imports must namespace-qualify decision references
- Imported decision services cannot have the same name, even if in different models
-
Recommend test-driven development:
- Suggest writing Bruno tests before implementing DMN logic
- Tests are in
test/bdt/and should mirror DMN file structure - Each test file should focus on one scenario
-
Hot reload workflow:
- DMN changes are picked up automatically by Quarkus dev mode
- No manual compilation needed
- Endpoint appears immediately in Swagger UI
IMPORTANT: Most Java code in target/generated-sources/kogito/ is auto-generated by Kogito. DO NOT edit these files.
Custom Java code (safe to edit):
src/main/java/org/codeforphilly/bdt/api/- REST endpoints and OpenAPI generation (~1,828 LOC)src/main/java/org/codeforphilly/bdt/functions/- Custom FEEL functions (~242 LOC)
Key classes to understand:
-
ModelRegistry.java (~554 LOC) - Discovers DMN models at startup
@Startupsingleton that runs at application start- Scans classpath for all
.dmnfiles (works in dev and JAR) - Parses DMN XML to extract metadata
- Validates model name uniqueness
- Caches ModelInfo objects for fast lookup
- Provides
findByPath()for endpoint routing
-
DynamicDMNResource.java (~150 LOC) - Main REST controller
- Handles all requests via
POST /api/v1/{path} - Routes requests to appropriate DMN models via ModelRegistry
- Builds DMN context from request JSON
- Evaluates decision services using KIE DMN engine
- Returns merged input+output context as JSON
- Handles all requests via
-
DynamicDMNOpenAPIFilter.java (~717 LOC) - Generates OpenAPI documentation
- Implements MicroProfile OpenAPI filter
- Generates OpenAPI path operations for each DMN model
- Creates request/response schemas from DMN types
- Groups endpoints by category (Benefits, Age Checks, etc.)
-
DMNSchemaResolver.java (~289 LOC) - Type schema resolver
- Converts DMN type definitions to JSON schemas
- Handles complex types, collections, nested contexts
- Generates example values for OpenAPI docs
-
LocationService.java (~84 LOC) - SQLite database lookup
- CDI bean providing static
lookup()method - Queries embedded SQLite database (
data/locations.db) - Callable from FEEL expressions in DMN files
- Example:
LocationService.lookup("zipCode", {"city": "Philadelphia"})
- CDI bean providing static
-
DecisionServiceInvoker.java (~158 LOC) - Dynamic invocation
- Programmatic DMN decision service invocation
- Used for dynamic invocation without static imports
- Maintains registry of known models and namespaces
- Not currently usable from FEEL (CDI limitation)
What Kogito Generates:
- REST resource classes (e.g.,
BenefitsResource.java,AgeResource.java) - Application configuration classes
- DMN runtime wrappers
What This Project Uses Instead:
- Custom
DynamicDMNResourcefor all REST endpoints - Configuration sets
kogito.generate.rest.decisions=false - Generated code exists but is not used for REST APIs
Why: Custom endpoints provide:
- Better URL patterns (matching file structure)
- Consistent response format (input+output merged)
- Custom OpenAPI documentation
- Path-based organization
-
Duplicate model names: All DMN models must have globally unique names. Validated at startup.
-
Incorrect service naming: Decision Service name must exactly match
{ModelName}Servicepattern or endpoint won't be generated. -
Missing namespace qualifiers: When calling imported decision services, always use namespace:
ImportedModel.ServiceName(...) -
Editing generated code: Never suggest editing files in
target/generated-sources/kogito/ -
Assuming Kogito defaults: This project uses custom endpoints, not Kogito's default REST generation
-
Breaking hot reload: Java changes require restart; only DMN changes hot reload
-
Circular imports: DMN files cannot have circular import dependencies
-
Type mismatches: Ensure imported types are properly qualified with namespace
Java tests (src/test/java/org/codeforphilly/bdt/api/):
- Purpose: Validate internal behavior (model discovery, endpoint patterns, OpenAPI generation)
- NOT for: Testing DMN business logic or specific eligibility rules
- Test classes:
ModelDiscoveryTest.java- DMN model discovery, naming conventions, OpenAPI generationDynamicEndpointPatternTest.java- Path mapping, URL patternsSituationTypeValidationTest.java- tSituation type consistencyOpenAPISchemaPatternTest.java- OpenAPI schema generation
- Run with:
mvn test
Bruno API tests (test/bdt/):
- Purpose: Validate API behavior and DMN decision logic
- Structure:
collection.bru- Collection config (host: http://localhost:8083/api/v1)environments/- Test environments (local dev, prod)benefits/- Benefit endpoint testschecks/- Check endpoint tests
- Organization: Test folders mirror DMN file paths
- Run with:
cd test/bdt && bru run
When adding new DMN models, always suggest creating corresponding Bruno tests.
Checks are reusable eligibility logic components.
Structure:
Decision Service: {CheckName}Service
├── Input: situation (tSituation)
├── Input: parameters (context with check-specific params)
└── Output: result (boolean)
File location: src/main/resources/checks/{category}/{check-name}.dmn
Endpoint: POST /api/v1/checks/{category}/{check-name}
Example: PersonMinAge check
- Model: "PersonMinAge"
- Service: "PersonMinAgeService"
- File:
checks/age/person-min-age.dmn - Endpoint:
/api/v1/checks/age/person-min-age - Input:
{situation, parameters: {minAge: number}} - Output:
{result: boolean}
Implementation pattern:
- Extract primary person from
situation.people - Call "age as of date" BKM from BDT.dmn
- Compare age to
parameters.minAge - Return true/false
Benefits compose multiple checks into specific program eligibility rules.
Structure:
Decision Service: {BenefitName}Service
├── Input: situation (tSituation)
└── Outputs:
├── checks (context with individual check results)
└── isEligible (boolean - typically all(checksAsList))
File location: src/main/resources/benefits/{jurisdiction}/{benefit-name}.dmn
Endpoint: POST /api/v1/benefits/{jurisdiction}/{benefit-name}
Response type: tBenefitResponse
{
"situation": { /* echoed back */ },
"checks": {
"checkName1": boolean,
"checkName2": boolean,
"checkName3": boolean
},
"isEligible": boolean
}Example: PhlHomesteadExemption
- Checks:
age65Plus: Calls PersonMinAgeService with minAge=65ownerOccupant: Readssituation.simpleChecks.ownerOccupantlivesInPhiladelphia: Readssituation.simpleChecks.livesInPhiladelphiaPanotEnrolledInHomestead: Calls PersonNotEnrolledInBenefitService
- Eligibility:
all([age65Plus, ownerOccupant, livesInPhiladelphia, notEnrolledInHomestead])
Implementation pattern:
- Create decision for each eligibility criterion
- Aggregate checks into
checkscontext decision - Create
isEligibledecision usingall([...])on checks - Include both in Decision Service outputs
Standard input type for all decisions. Represents household circumstances.
Core fields:
tSituation {
primaryPersonId: string // ID of person being evaluated
people: tPersonList // Array of household members
[{
id: string
dateOfBirth: date
}]
enrollments: tEnrollmentList // Current benefit enrollments
[{
personId: string
benefit: string
}]
relationships: tRelationshipList // Household relationships
[{
type: string // e.g., "spouse", "child"
personId: string
relatedPersonId: string
}]
simpleChecks: tSimpleChecks // Boolean flags for common checks
{
ownerOccupant: boolean
livesInPhiladelphiaPa: boolean
// ... other simple yes/no checks
}
}
Extensible: New fields can be added to tSituation in BDT.dmn as needed.
Usage patterns:
- Extract primary person:
situation.people[item.id = situation.primaryPersonId] - Find specific person:
situation.people[item.id = "someId"] - Check enrollment:
count(situation.enrollments[item.benefit = "benefitName"]) > 0 - Access simple checks:
situation.simpleChecks.ownerOccupant
-
Plan the benefit:
- Identify eligibility criteria
- Determine which checks are reusable vs. inline
- Decide on jurisdiction path (e.g.,
pa/phl/for Philadelphia, PA)
-
Create test cases first (TDD):
- Create folder:
test/bdt/benefits/{jurisdiction}/{BenefitName}/ - Write tests:
Smoke Test.bru- Basic happy pathEligible - {Scenario}.bru- Valid scenariosIneligible - {Reason}.bru- Each failing criterionEdge Case - {Description}.bru- Boundary conditions
- Create folder:
-
Create DMN file:
- Location:
src/main/resources/benefits/{jurisdiction}/{benefit-name}.dmn - Use VS Code with DMN Editor extension
- Location:
-
Set up imports:
- Import BDT.dmn (for tSituation and shared types)
- Import Benefits.dmn (for tBenefitResponse)
- Import needed check DMNs (e.g., person-min-age.dmn)
-
Define Decision Service:
- Name:
{BenefitName}Service(CRITICAL: must follow pattern) - Input:
situation(type: tSituation from BDT) - Outputs:
checks(context),isEligible(boolean)
- Name:
-
Implement check decisions:
- For reusable checks:
CheckModel.CheckService(situation, {params}).result - For inline checks: FEEL expressions using
situationfields - Name each decision descriptively (e.g.,
age65Plus,incomeLimit)
- For reusable checks:
-
Create checks context decision:
{ "checkName1": checkName1Decision, "checkName2": checkName2Decision, "checkName3": checkName3Decision } -
Create isEligible decision:
all([checkName1, checkName2, checkName3]) -
Test:
- Save DMN file
- Check Swagger UI for new endpoint
- Run Bruno tests:
cd test/bdt && bru run benefits/{jurisdiction}/{BenefitName} - Iterate on DMN logic based on test failures
-
Document (optional):
- Add description to DMN model metadata
- Add comments in Bruno tests explaining scenarios
-
Determine category:
- Existing:
age,enrollment - New category: Create base module (see below)
- Existing:
-
Create base module (if new category):
- Location:
src/main/resources/checks/{category}/{Category}.dmn - Define category-specific types (e.g.,
tIncomeSource,tIncomeList) - Import BDT.dmn
- Provide BKMs for common operations if needed
- Location:
-
Create check DMN:
- Location:
src/main/resources/checks/{category}/{check-name}.dmn - Import BDT.dmn and relevant base module
- Location:
-
Define Decision Service:
- Name:
{CheckName}Service - Inputs:
situation(tSituation)parameters(context with check-specific params)
- Output:
result(boolean)
- Name:
-
Implement logic:
- Use FEEL expressions
- Reference
situationfields andparameters - Call BKMs from BDT.dmn or base modules as needed
-
Create Bruno tests:
- Folder:
test/bdt/checks/{category}/{CheckName}/ - Tests:
Pass.bru- Check succeedsFail.bru- Check fails- Edge cases as needed
- Folder:
-
Test:
- Save DMN file
- Endpoint:
/api/v1/checks/{category}/{check-name} - Run Bruno tests:
cd test/bdt && bru run checks/{category}/{CheckName}
-
Use in benefits:
- Import check DMN in benefit file
- Call:
CheckModel.CheckService(situation, {param1: value1}).result
Workflow:
- Open DMN in VS Code with DMN Editor extension
- Make changes (edit logic, add/remove decisions, modify types)
- Save (Ctrl+S or Cmd+S)
- Watch Quarkus console logs for "Reloading..."
- Test immediately in Swagger UI or rerun Bruno tests
Tips:
- Keep Swagger UI open to see updated schemas
- Use Bruno tests to verify changes didn't break existing behavior
- For complex changes, access raw XML via "Reopen with Text Editor"
- Test individual decisions in Swagger UI before composing in benefits
Common modifications:
- Add a check to benefit: Add new decision → Update
checkscontext → UpdateisEligiblelogic - Change check logic: Modify decision expression → Save → Test
- Add parameter: Update Decision Service inputs → Update logic → Update Bruno tests
- Refactor inline check to reusable: Create new check DMN → Import in benefit → Replace inline logic with call
Symptoms: DMN file saved, but endpoint doesn't show in Swagger UI
Checklist:
- DMN file is in
src/main/resources/(or subdirectory) - Decision Service is named
{ModelName}Serviceexactly - Model name is unique (no duplicate model names)
- Quarkus dev mode is running (
bin/dev) - Check startup logs for model discovery messages
Common causes:
- Service named incorrectly (e.g.,
PersonMinAgeDecisioninstead ofPersonMinAgeService) - Model name conflicts with existing model
- DMN file has XML syntax errors
- File not in resources directory
How to diagnose:
- Check Quarkus logs during startup for "Discovered X DMN models"
- Look for warnings about duplicate model names
- Verify DMN file opens in VS Code DMN Editor without errors
- Check
ModelRegistrylogs for discovery failures
Symptoms: Swagger UI shows unexpected schema, or API returns 400 errors
Checklist:
- Decision Service inputs/outputs are defined correctly
- Imported types use correct namespace prefix
- Type names match exactly (case-sensitive)
- tSituation structure matches expected format
Common causes:
- Typo in type name (e.g.,
tSituationsinstead oftSituation) - Missing namespace prefix on imported types
- Type defined in wrong DMN file
- Field name mismatch in FEEL expressions
How to diagnose:
- Check Swagger UI for actual expected schema
- Compare with DMN Decision Service definition
- Verify imports are correctly configured
- Test with minimal request body first
Symptoms: API returns 500 error, or results are incorrect
Checklist:
- FEEL expression syntax is valid
- Variable names match Decision names exactly
- Imported decisions are called with namespace:
ModelName.ServiceName(...) - All required parameters are provided
Common causes:
- Typo in variable name (e.g.,
age65plusinstead ofage65Plus) - Missing namespace prefix on imported decision call
- Incorrect parameter structure
- Division by zero or null pointer in FEEL
How to diagnose:
- Check Quarkus dev logs (shows DMN evaluation details)
- Test individual decisions in Swagger UI first
- Simplify FEEL expression to isolate issue
- Use FEEL playground tools to test expressions
Debugging tips:
- Add intermediate decisions to inspect values
- Use simple FEEL expressions first, then make complex
- Test imported decisions independently before composing
Symptoms: Cannot reference decision from imported DMN file
Checklist:
- Imported DMN file is in correct location
- Import statement includes proper namespace URI
- Decision references use namespace prefix:
ImportedModel.DecisionName - No circular imports
Common causes:
- Incorrect namespace URI in import statement
- Missing namespace prefix in decision reference
- Circular import (A imports B, B imports A)
- Imported file has DMN errors
How to diagnose:
- Open imported DMN file in VS Code DMN Editor
- Verify namespace URI matches import statement
- Check for import warnings in Quarkus logs
- Test imported decision independently
Fix patterns:
- Always use namespace prefix:
PersonMinAge.PersonMinAgeService(...) - Copy namespace URI from imported DMN file's properties
- Break circular imports by extracting shared types to third file
Symptoms: DMN changes not picked up, old logic still runs
Checklist:
- File saved (check editor for unsaved indicator)
- Quarkus dev mode is running
- No compilation errors in console
- Browser cache cleared (for Swagger UI)
Common causes:
- File not saved
- Quarkus dev mode crashed (check console)
- DMN has XML syntax errors preventing reload
- Java changes mixed with DMN changes (requires restart)
How to fix:
- Stop and restart dev server:
bin/dev - Clean rebuild:
mvn clean compile - Check console for compilation errors
- Save file again and watch for reload message
Note: Only DMN changes hot reload. Java code changes require full restart.
Symptoms: Bruno tests fail after DMN changes
Checklist:
- Dev server is running at http://localhost:8083
- Environment set to "local dev" in Bruno
- Request body JSON is valid
- Assertions match current response format
Common causes:
- Dev server not running
- Wrong environment selected
- Response format changed (e.g.,
result→checkResult) - Added new required field to tSituation
How to fix:
- Verify dev server:
curl http://localhost:8083/q/health - Check Bruno environment settings
- Update test assertions to match new response format
- Add new required fields to test request bodies
Tip: Run individual test first to isolate issue: cd test/bdt && bru run path/to/test.bru
Symptoms: Docker build errors during deployment
Checklist:
mvn clean packagesucceeds locallytarget/quarkus-app/directory exists- Docker daemon is running
- Dockerfile path is correct
Common causes:
- Maven build failed (check pom.xml)
- Missing dependencies
- Wrong Dockerfile path
- Insufficient disk space
How to fix:
- Run
mvn clean packageand fix any errors - Verify
target/quarkus-app/quarkus-run.jarexists - Check Docker is running:
docker ps - Build locally first:
docker build -f src/main/docker/Dockerfile.jvm -t test .
Symptoms: "Model not found" errors at runtime
Checklist:
- Path matches file location exactly
- File extension is
.dmn - ModelRegistry discovered the model (check startup logs)
- Model name matches path expectation
Common causes:
- Path mismatch (e.g.,
/checks/age/person-min-agebut file isperson_min_age.dmn) - File in wrong directory
- Model not discovered due to syntax errors
- Casing issue (paths are case-sensitive)
How to diagnose:
- Check startup logs: "Discovered 8 DMN models: ..."
- Verify file path matches endpoint path
- Test in Swagger UI to see if endpoint exists
- Check ModelRegistry for the model
These are strictly enforced and violations will cause runtime errors:
-
Model Name Uniqueness:
- All DMN models must have unique names (validated at startup)
- Applies across all DMN files, even in different directories
- Violation: Application fails to start with error message
-
Service Naming:
- Decision Services must follow
{ModelName}Servicepattern exactly - Case-sensitive, no spaces, no variations
- Violation: Endpoint not generated, model not accessible via API
- Decision Services must follow
-
No Duplicate Service Names:
- Imported decision services cannot share names
- Even if in different DMN models
- Violation: Ambiguous references, evaluation errors
-
Namespace Qualification:
- All imported decision references must use namespace prefix
- Format:
ImportedModel.DecisionName - Violation: Decision not found, evaluation fails
-
Java Version:
- Must be Java 17 (not 21 like main BDT project)
- Red Hat UBI 9 OpenJDK
- Violation: Build failures, runtime errors
-
Kogito Version:
- Locked to 1.44.1.Final (compatibility with Quarkus 2.16.10)
- Cannot upgrade without Quarkus upgrade
- Violation: Dependency conflicts, build failures
-
Hot Reload Scope:
- Works for DMN changes only
- Java changes require full restart
- Violation: Changes not picked up, old code runs
-
File Naming:
- Use kebab-case for DMN files (e.g.,
person-min-age.dmn) - Lowercase, hyphens, no spaces
- Violation: Inconsistency, harder to map to endpoints
- Use kebab-case for DMN files (e.g.,
-
Decision Service Required:
- Every exposed DMN model must have a Decision Service
- Named
{ModelName}Service - Violation: No endpoint generated
-
Path Mapping:
- Endpoint path must match file path exactly
/api/v1/{path-to-dmn-without-extension}- Violation: Model not found errors
- Java application framework
- Dev mode with hot reload (DMN only)
- MicroProfile OpenAPI integration
- Embedded Undertow server
- CDI for dependency injection
Key dependencies:
quarkus-kogito-decisions- DMN engine integrationquarkus-smallrye-openapi- OpenAPI documentationquarkus-jdbc-sqlite- Embedded database
- DMN 1.3 specification implementation
- KIE DMN engine for evaluation
- FEEL expression language
- Model compilation and caching
Limitations:
- Cannot upgrade independently of Quarkus
- Locked to specific KIE version
- Some FEEL functions not available
- LTS version
- Required for Quarkus 2.x compatibility
- Different from main BDT project (uses Java 21)
- Build tool
- Manages dependencies
- Profiles for dev/prod builds
- Embedded database for location lookups
- Located at
src/main/resources/data/locations.db - Accessed via
LocationService.lookup()
- API testing tool
- Collection-based organization
- Environment management
- CLI for CI/CD:
bru run
Source of truth: pom.xml version field
Synchronized across:
- Git tags:
library-api-v{version}(e.g.,library-api-v0.3.0) - Docker tags:
:v{version}and:latest - Cloud Run revisions:
library-api-v{version-with-dashes}(e.g.,library-api-v0-3-0)
Version guidelines:
- MAJOR (x.0.0): Breaking API changes
- Removing endpoints
- Changing response format
- Renaming models
- MINOR (0.x.0): New features (backwards compatible)
- New benefits/checks
- New endpoints
- New fields in tSituation
- PATCH (0.0.x): Bug fixes (backwards compatible)
- Fix DMN logic bugs
- Documentation updates
- Dependency updates
-
Update version:
./bin/tag-release 0.4.0- Updates
pom.xmlusing Maven - Commits change
- Creates annotated git tag
- Updates
-
Review:
git show -
Push:
git push origin your-branch git push origin library-api-v0.4.0
-
Automated deployment:
- GitHub Actions triggered by tag push
- Maven builds application
- Docker image built and pushed
- Cloud Run deployment with new revision
Triggered by: Tag push matching library-api-v*
Steps:
- Checkout code
- Set up Java 17
- Maven build:
mvn clean package - Validate version sync (tag vs pom.xml)
- Docker build using
src/main/docker/Dockerfile.jvm - Push to Google Artifact Registry with tags:
:v{version},:latest - Deploy to Cloud Run with revision name
Project: benefit-decision-toolkit-play
Region: us-central1
Service: library-api
Container Registry: us-central1-docker.pkg.dev/benefit-decision-toolkit-play/benefit-decision-toolkit-play/library-api
Settings:
- Max instances: 2
- Authentication: Allow unauthenticated (public API)
- Port: 8080 (Cloud Run default)
- CPU: 1
- Memory: 512Mi
Service Account: library-api-service-account@benefit-decision-toolkit-play.iam.gserviceaccount.com
Database: src/main/resources/data/locations.db
Schema:
- Tables: zip_codes, cities, counties, states
- Columns vary by table
- Supports lookup by any column with filters
Usage from FEEL:
// Look up ZIP code by city
LocationService.lookup("zipCode", {"city": "Philadelphia", "state": "PA"})
// Returns: ["19102", "19103", "19104", ...]
// Look up county by ZIP
LocationService.lookup("county", {"zipCode": "19102"})
// Returns: "Philadelphia"
Implementation:
- CDI-managed bean with
@ApplicationScoped - Datasource injected via
@Named("locations") - Constructs dynamic SQL queries from filter map
- Returns list of strings (empty list if no matches)
- Thread-safe for concurrent FEEL evaluations
Adding new lookups:
- Add data to SQLite database (use DB Browser for SQLite)
- No code changes needed (dynamic query construction)
- Use in DMN:
LocationService.lookup("yourColumn", {"filter": "value"})
Purpose: Programmatic invocation of DMN decision services at runtime without static imports
Use case: Dynamic benefit evaluation where you don't know which benefits to check until runtime
Java usage (via CDI):
@Inject
DecisionServiceInvoker invoker;
public void evaluate() {
Map<String, Object> situation = createSituation();
Map<String, Object> params = Map.of("minAge", 65);
Object result = invoker.invokeInternal(
"benefits", // Model name
"PhlHomesteadExemption", // Decision service name
situation,
params
);
}Model registry:
known.put("benefits", "https://kie.apache.org/dmn/benefits-namespace");
known.put("age", "https://kie.apache.org/dmn/age-namespace");Limitations:
- Not currently callable from FEEL expressions (CDI container limitations)
- Models must be manually registered in
getKnownModels() - No auto-discovery of available models
Future enhancements:
- Auto-discovery from classpath
- Configuration-based registration (application.properties)
- FEEL-accessible wrapper function
Built-in FEEL functions:
// Date/time
date("2025-01-01")
today()
duration("P1Y") // 1 year
// Lists
count([1, 2, 3]) // 3
sum([1, 2, 3]) // 6
all([true, true, false]) // false
// Strings
substring("hello", 1, 2) // "he"
upper case("hello") // "HELLO"
Custom Java functions from FEEL:
Requirements:
- Must be
public static - Must be in a class on the classpath
- Use full class path in FEEL
Example:
package org.codeforphilly.bdt.functions;
public class LocationService {
public static List<String> lookup(String column, Map<String, Object> filters) {
// implementation
}
}Called from FEEL:
org.codeforphilly.bdt.functions.LocationService.lookup("zipCode", {"city": "Philadelphia"})
Best practices:
- Keep FEEL expressions simple and readable
- Use Java functions for complex logic, database access, external APIs
- Test Java functions independently before using in DMN
- Document expected input/output types clearly
How it works:
- At startup: Filter runs during OpenAPI schema generation
- Scans ModelRegistry: Gets all discovered DMN models
- Generates paths: Creates OpenAPI path for each model
- Resolves schemas: Converts DMN types to JSON schemas
- Groups endpoints: Categorizes by Benefits, Checks, etc.
- Adds examples: Generates sample request/response JSON
Customizing:
Edit DynamicDMNOpenAPIFilter.java:
if (modelInfo.getModelName().equals("PersonMinAge")) {
operation.setDescription("Custom description for PersonMinAge");
}Schema generation (DMNSchemaResolver):
- Simple types: string, number, boolean, date → JSON primitives
- Collections: tPersonList → array of tPerson objects
- Contexts: Complex DMN types → JSON objects
- Nested types: Resolves imported type references
Direct users to README.md for:
- Quick start and getting started
- High-level architecture overview
- Common development tasks (adding benefits/checks)
- Testing overview
- Deployment quick reference
- Key commands and configuration
This CLAUDE.md provides:
- Detailed architecture and implementation patterns
- Comprehensive troubleshooting guide
- AI-specific guidance and common pitfalls
- In-depth technical details
- Java class descriptions and line counts
- Advanced topics and future enhancements
Last Updated: 2025-01-21 | README Version: 0.3.0