A Quarkus application that automatically generates REST APIs from DMN (Decision Model and Notation) files. DMN files are "source code" for JSON web APIs—add a DMN file, get a REST endpoint.
Use VS Code w/ the DMN Editor Extension
The models are in src/main/resources
cd library-api
bin/devServes at http://localhost:8083 with:
- Swagger UI: http://localhost:8083/q/swagger-ui
- OpenAPI spec: http://localhost:8083/q/openapi
curl -X POST http://localhost:8083/api/v1/checks/age/person-min-age \
-H "Content-Type: application/json" \
-d '{
"situation": {
"primaryPersonId": "p1",
"people": [{"id": "p1", "dateOfBirth": "1950-01-01"}]
},
"parameters": {"minAge": 65, "personId": "p1", "asOfDate": "2025-01-01"}
}'Returns: {"checkResult": true, ...}
Explore all endpoints in Swagger UI or check test/bdt/ for more examples.
New to DMN? Learn DMN in 15 Minutes
Each DMN file in src/main/resources/ becomes a REST endpoint:
File: src/main/resources/checks/age/person-min-age.dmn
└─ Decision Service: PersonMinAgeService
└─ Generates: POST /api/v1/checks/age/person-min-age
Pattern: POST /api/v1/{path-to-dmn-without-extension}
Checks = Reusable eligibility logic (e.g., "is person 65+?")
- Location:
src/main/resources/checks/{category}/ - Example:
checks/age/person-min-age.dmn→/api/v1/checks/age/person-min-age
Benefits = Specific programs that compose checks (e.g., "Homestead Exemption")
- Location:
src/main/resources/benefits/{jurisdiction}/ - Example:
benefits/pa/phl/homestead-exemption.dmn→/api/v1/benefits/pa/phl/homestead-exemption
Standard input for all endpoints. The situation upon which you'd like to compute some benefit's eligibility:
{
"situation": {
"primaryPersonId": "p1", // Person being evaluated
"people": [{ // Household members
"id": "p1",
"dateOfBirth": "1950-01-01"
}],
"enrollments": [{ // Current benefits
"personId": "p1",
"benefit": "homestead"
}],
"simpleChecks": { // Boolean flags
"ownerOccupant": true,
"livesInPhiladelphiaPa": true
}
}
}- Decision Service naming: Must be
{ModelName}Service- Model "PersonMinAge" → Service "PersonMinAgeService"
- Model names must be unique across all DMN files
- File names: Use kebab-case (
person-min-age.dmn)
Breaking these rules = endpoint won't appear.
- Create DMN file:
src/main/resources/benefits/{jurisdiction}/{name}.dmn - Import: BDT.dmn, Benefits.dmn, any check DMNs you need
- Define Decision Service:
{BenefitName}Service - Implement checks (call reusable checks or inline logic)
- Create
checkscontext andisEligibledecision - Save → endpoint appears at
/api/v1/benefits/{jurisdiction}/{name}
Example benefit response:
{
"situation": {...},
"checks": {
"age65Plus": true,
"ownerOccupant": true,
"livesInPhiladelphia": false
},
"isEligible": false
}- Create DMN:
src/main/resources/checks/{category}/{name}.dmn - Import BDT.dmn (and relevant base modules)
- Define Decision Service:
{CheckName}Service - Input:
situation+parameters, Output:result(boolean) - Save → endpoint appears at
/api/v1/checks/{category}/{name}
DMN changes are picked up automatically:
- Edit DMN file in VS Code (DMN Editor extension)
- Save
- Check Swagger UI—endpoint updates immediately
Java changes require restart.
- Endpoint missing? Check Decision Service name matches
{ModelName}Servicepattern - Evaluation errors? Check Quarkus logs (shows DMN evaluation details)
- Import issues? Use namespace-qualified references:
ImportedModel.ServiceName(...)
Test individual decisions in Swagger UI before composing them.
Purpose: Validate internal behavior (model discovery, endpoint patterns)
- NOT for testing DMN business logic
- Location:
src/test/java/org/codeforphilly/bdt/api/
Purpose: Validate DMN logic and API behavior
- Test structure mirrors DMN structure
- Location:
test/bdt/benefits/,test/bdt/checks/ - Create Bruno tests for every new benefit/check
Workflow: Write Bruno tests first (TDD), then implement DMN.
# Update version in pom.xml and create git tag atomically
cd library-api
./bin/tag-release 0.4.0
# Review changes
git show
# Push to trigger deployment
git push origin your-branch
git push origin library-api-v0.4.0Pushing the tag triggers GitHub Actions → Docker build → Google Cloud Run deployment.
Semantic versioning:
- MAJOR: Breaking changes (removing endpoints, changing response format)
- MINOR: New features (new benefits/checks)
- PATCH: Bug fixes
Production: Google Cloud Run, us-central1, public API
| Technology | Version | Purpose |
|---|---|---|
| Java | 17 | Runtime |
| Quarkus | 2.16.10.Final | Framework |
| Kogito | 1.44.1.Final | DMN engine |
| Maven | 3.8+ | Build tool |
| DMN | 1.3 | Decision modeling |
| Bruno | Latest | API testing |
# Development
bin/dev # Start dev server
mvn clean compile # Clean rebuild
mvn clean package # Build JAR
# Testing
mvn test # Java tests
cd test/bdt && bru run # Bruno tests
# Deployment
./bin/tag-release 0.4.0 # Create release- All DMN model names must be globally unique
- Decision Services must be named
{ModelName}Service - Java 17 required (not 21 like main BDT project)
- Hot reload works for DMN only; Java changes need restart
- Imported decision services can't share names
- Port: Set
QUARKUS_HTTP_PORT(default: 8083)
src/main/resources/BDT.dmn- Root model with shared typessrc/main/resources/checks/- Reusable eligibility checkssrc/main/resources/benefits/- Specific benefit programssrc/main/java/org/codeforphilly/bdt/api/- Custom REST endpointstest/bdt/- Bruno API tests
- Learn DMN: https://learn-dmn-in-15-minutes.com/
- Kogito Docs: https://kogito.kie.org/
- Quarkus Guides: https://quarkus.io/guides/
- CLAUDE.md: Detailed architecture and AI assistant guidance
- GitHub: https://github.com/codeforphilly/benefit-decision-toolkit
This project uses custom endpoint generation (not Kogito's defaults):
DynamicDMNResource.javahandles all requests viaPOST /api/v1/{path}ModelRegistry.javadiscovers DMN models at startupDynamicDMNOpenAPIFilter.javagenerates OpenAPI docs
Why? Better URL patterns (match file structure), consistent response format, custom OpenAPI.
src/main/java/org/codeforphilly/bdt/api/- REST + OpenAPIsrc/main/java/org/codeforphilly/bdt/functions/- Custom FEEL functionssrc/main/resources/*.dmn- DMN decision models
DO NOT EDIT: target/generated-sources/kogito/ (auto-generated)
- DecisionServiceInvoker: Programmatic DMN invocation (Java only, not from FEEL)
- Custom OpenAPI: Type schemas auto-generated from DMN types
See CLAUDE.md for detailed architecture, troubleshooting, and development patterns.
Version: 0.4.0 | Last Updated: 2025-01-21