Baptiste Mesta
Dev @ Bonitasoft for 10 years
BPM Execution engine
Developer tooling
IA/Analytics module
Loan request application
Low code development
Visual representation of the process
Business data editor
WYSIWYG editor to create pages and forms
Platform to administrate processes / users
Out of the box user case list
I want everything as code in one place
A DSL to code processes and execute them using an Embedded engine
The basic idea of a domain specific language (DSL) is a computer language that’s targeted to a particular kind of problem, rather than a general purpose language that’s aimed at any kind of software problem.
aka "Create a new language from scratch"
aka "I don’t want to spend 2 months on that"
Spock framework
def "should return user given its id and tenant id"() {
given:
def expectedUser = aUser(new User(id: 1))
when:
def user = repository.findById(1).get()
then:
user == expectedUser
}
Gradle DSL
plugins {
`kotlin-dsl`
}
repositories {
jcenter()
}
dependencies {
compile("commons-httpclient:commons-httpclient:3.1")
}
kotlin is…
cross-platform
statically typed
executable on the JVM
designed to interoperate fully with Java
concise
html {
head {
title {+"Some title"}
}
body {
h1 {+"Some title"}
p {+"Some text"}
a(href = "http://kotlinlang.org") {+"Kotlin"}
}
}
+
fun html(init: HTML.() -> Unit): HTML {
val html = HTML() // create the receiver object
html.init() // pass the receiver object to the lambda
return html
}
@DslMarker
annotation class ProcessDSLMarker
html {
head {
head {} // should be forbidden
}
// ...
}
The general rule:
- an implicit receiver may *belong to a DSL @X*
if marked with a corresponding DSL marker annotation
- two implicit receivers of the same DSL
are not accessible in the same scope
- the closest one wins
- other available receivers are resolved as usual,
but if the resulting resolved call binds to such a receiver,
it's a compilation error
fun sum(a: Int, b: Int) = a + b
fun data(init: DataContainer.() -> Unit) {
DataContainer(builder).init()
}
becomes
fun data(init: DataContainer.() -> Unit) = DataContainer(builder).init()
// Bad
fun foo() = foo("a")
fun foo(a: String) { ... }
// Good
fun foo(a: String = "a") { ... }
automaticTask("name"){
}
becomes
automaticTask("name")
infix fun Int.shl(x: Int): Int { ... }
// calling the function using the infix notation
1 shl 2
// is the same as
1.shl(2)
text("type").describedAs("type of the loan")
becomes
text("type") describedAs "type of the loan"
fun MutableList<Int>.swap(index1: Int, index2: Int) {
val tmp = this[index1] // 'this' corresponds to the list
this[index1] = this[index2]
this[index2] = tmp
}
actor("requester") mappedToUser "john"
becomes
"requester" mappedToUser "john"
val isEmpty: Boolean
get() = this.size == 0
text("type") describedAs "type of the loan"
becomes
text named "type" describedAs "type of the loan"
(name not official)
Leverage extension function to create extensible DSLs
Spring boot configuration
@ConditionalOnResource(resources = "classpath:process")
Programmatically execute script
ScriptEngine scriptEngine = scriptEngineManager
.getEngineByExtension("kts");
BusinessArchive bar = (BusinessArchive) scriptEngine.eval(content);
deployBar(apiClient, bar);
Use Kotlin… 90% of the job is done
Write how it should look like first
process {
data {
text named "myData"
}
}
Don’t try to use all features
Focus on usability and discovery
Throw away your POCs
Once your confortable rewrite everything
A good way to learn kotlin
@bonitasoft @baptistemesta
#Bonitasoft @baptistemesta