Skip to content

navikt/rule-dsl

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

78 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

rule-dsl nav.no logo

Purpose

Provides a lightweight framework to create, run and explain rules. Any application that needs rules coded in a structured manner may use this. The goal is to isolate functional rules from the technical code and thus make the rules more accessible to non-technical personnel.

Inspired by dp-quiz.

Documentation

Components

A set of classes inheriting AbstractRuleComponent make up the core of the framework:

Treestructure

All AbstractRuleComponent are organized in a visitor-accepting tree structure. This tree enables tracking of the context a rulecomponent was executed in.

regeltjeneste: BeregnAlderspensjonService
  regelflyt: BeregnAlderspensjonFlyt
    regelsett: BeregnFaktiskTrygdetidRS
      regel: JA BeregnFaktiskTrygdetidRS.SettFireFemtedelskrav
      regel: NEI BeregnFaktiskTrygdetidRS.Skal ha redusert fremtidig trygdetid
        NEI 'virkningstidspunkt' (1990-05-01) må være etter eller lik '1991-01-01'
        JA 'faktisk trygdetid i måneder' (224) er mindre enn 'firefemtedelskrav' (480)

Noteworthy methods are:

See InspectionTest for examples.

Resource

AbstractRuleComponents have a resourceMap containing AbstractResource instantiated per service call. These objects typically contain resources like rates ("satser"), loggers and other global assets. See AbstractDemoRuleService for demonstration.

DSL

A kotlin "mini-DSL", inspired by Kotlins type-safe builders, provides a simple syntax for creating rules and describing logic flow control in ruleflows. The domain in the DSL is generic ruledevelopment and not specific to NAV.

All DSL syntax is in norwegian.

In AbstractRuleflow:

forgrening("Sivilstand gift?") {
    gren {
      /**
       *  Et boolsk uttrykk betinger eksekveringen av påfølgende flyt-blokk.
       */
        betingelse { parameter.input.person.erGift }
        flyt {
            grunnpensjonSats = 0.90
        }
    }
    gren { } // andre gren
}

A standard rule in AbstractRuleset with technical predicate and functional Subsumtion. A Functional Subsumtion is any expression that produces an object of type AbstractSubsumsjon. See custom Operators.

regel("RedusertTrygdetid") {
  HVIS { trygdetid != null }         // technical predicate
  OG { trygdetid erMindreEnn 40 }    // functional subsumtion
  SÅ {
      netto = grunnbeløp * sats * trygdetid / 40.0
  }
}

A rule with else-statement and a value return:

regel("Trygdetid") {
  HVIS { anvendtFlyktning erLik OPPFYLT }
  SÅ {                  // action-statement runs if all predicates are true
    RETURNER( 40 )      // returns a value and stops further evaluation of the ruleset.
  }
  ELLERS {              // else-statement runs if one or more predicates are false
    RETURNER( faktiskTrygdetid )
  }
}

Pattern

Optional feature for writing rules on lists. A Pattern object wraps a List and ensures a Rule instance is created for each item in the list.

val norskeBoperioder = boperiodeListe.createPattern { it.land == LandEnum.NOR }

regel("BoPeriodeStartFør16år", norskeBoperioder) { boperiode ->
  HVIS { boperiode.fom < dato16år }
  SÅ {
    svar.faktiskTrygdetidIMåneder += ChronoUnit.MONTHS.between(dato16år, boperiode.tom)
  }
}

Visualization

A rudimentary plugin for Intellij is in development for ruleflow visualization.

Usage

Maven:

<dependency>
  <groupId>no.nav.system</groupId>
  <artifactId>rule.dsl</artifactId>
  <version>1.4.7</version>
</dependency>

Contact

External: Raise issues on GitHub

Internal: On slack #pensjon-regler