Onboarding
Welcome to the Team 🚀
This document guides you step by step through the key technologies and tools of our tech stack. The order is intentional: first you learn the domain fundamentals, then the technical platform, and finally our programming language and framework.
Fundamentals: BPMN & DMN
Before diving into the tools, it's important to understand the underlying standards. BPMN and DMN are the language we use to describe business processes and decision logic.
What is BPMN?
Business Process Model and Notation (BPMN) is a graphical standard for modeling business processes. It defines a unified notation that is understood by both domain experts and developers.
What is DMN?
Decision Model and Notation (DMN) complements BPMN with the modeling of decision logic – typically in the form of Decision Tables.
Installation & Tools (Mac)
# Option 1: Camunda Modeler (recommended – supports BPMN & DMN)
brew install --cask camunda-modeler
Or download manually: https://camunda.com/download/modeler/
Further Reading
- 📖 BPMN Specification (OMG)
- 📖 DMN Specification (OMG)
- 🎓 BPMN Tutorial – Camunda
- 🎓 DMN Tutorial – Camunda
Process Engines – Overview
Orchescala is engine-agnostic – it supports multiple BPMN engines through a unified abstraction. For onboarding, it's important to understand the differences.
Supported Engines
| Engine | Description | Status | Architecture |
|---|---|---|---|
| Camunda 7 | The classic Camunda platform | Production | Embedded or standalone engine, REST API |
| Camunda 8 | Zeebe-based cloud-native engine | Proof of Concept | Distributed engine, gRPC + REST API |
| Operaton | Open-source fork of Camunda 7 | Proof of Concept | Same as Camunda 7 (compatible REST API) |
Camunda 7
The classic Camunda platform – battle-tested for years and our production standard.
Core Concepts:
- Process Engine: Executes BPMN processes
- Tasklist: UI for human tasks (User Tasks)
- Cockpit: Monitoring & Operations
- REST API: External systems interact via HTTP
- External Tasks: Worker pattern for Service Tasks
Links:
Camunda 8
The next generation – cloud-native, based on Zeebe as a distributed workflow engine.
Key Differences from Camunda 7:
| Aspect | Camunda 7 | Camunda 8 |
|---|---|---|
| Engine | Embedded / Standalone | Zeebe (distributed engine) |
| Communication | REST API | gRPC + REST API |
| Variables | Java objects + JSON | JSON only |
| Expressions | JUEL | FEEL |
| Scripts | Inline Groovy/JS | FEEL or Workers |
| Tasklist | Classic Tasklist | New Tasklist (React) |
Links:
Operaton
Operaton is the open-source fork of Camunda 7, maintained by the community. The REST API is compatible with Camunda 7, allowing Orchescala to use the same client.
Why Operaton?
- 100% open source (no enterprise license required)
- API-compatible with Camunda 7
- Community-driven, independent development
Links:
Which Engine to Use?
| Use Case | Recommended Engine |
|---|---|
| Production processes (established) | Camunda 7 |
| New projects / cloud-native | Camunda 8 |
| Open source without license costs | Operaton |
Scala – User
As a user of Orchescala, you need to be able to read and write Scala to describe processes and domain objects – without diving deep into the infrastructure libraries. These two concepts and three domain libraries are sufficient for that.
Setup (Mac)
# Install Coursier (Scala Toolchain Manager)
brew install coursier/formulas/coursier
# Set up Scala toolchain (installs Java, Scala, sbt, scala-cli, etc.)
cs setup
# Install specific Java version (Temurin 21 LTS recommended)
cs java --jvm temurin:21 --setup
# Verify everything is installed
java -version
scala -version
sbt --version
💡cs setupautomatically installs a current JVM, Scala, sbt, and other useful tools. Usecs java --jvm temurin:21 --setupto set a specific JVM as the default.
If Coursier doesn't work – Homebrew fallback:
brew install --cask temurin@21
brew install scala sbt
# IntelliJ IDEA + Scala Plugin (recommended IDE)
brew install --cask intellij-idea
Basics
The fundamentals of Scala – type system, collections, pattern matching, etc.
Key Concepts:
val/var– immutable vs. mutable variables- Case Classes & Sealed Traits – algebraic data types
- Pattern Matching – more powerful than
switch - Option, Either, Try – type-safe error handling
- Collections API –
map,filter,flatMap,fold - For-Comprehensions – syntactic sugar for monads
Resources:
DSLs (Domain Specific Languages)
Orchescala provides its own DSL for describing processes and workers. Scala is excellent for this thanks to its flexible syntax.
Concepts:
- Extension Methods – extend existing types
- Operator Overloading – readable expressions
- Builder Pattern – fluent APIs
- Typeclass-based DSLs
Resources:
Iron
Iron is a Scala 3 library for Refined Types – types with embedded constraints that enable compile-time validation.
// Example: Type-safe amount – never negative
import io.github.iltotore.iron.*
import io.github.iltotore.iron.constraint.numeric.*
type PositiveAmount = Double :| Positive
val amount: PositiveAmount = 100.0.refineUnsafe
Resources:
Tapir
Tapir enables type-safe HTTP API definitions in Scala. Endpoints are described as values and can be interpreted as server, client, or OpenAPI documentation.
// Define endpoint
val bookListing: PublicEndpoint[Unit, String, List[Book], Any] =
endpoint.get
.in("books" / "list" / "all")
.errorOut(stringBody)
.out(jsonBody[List[Book]])
Resources:
Circe
Circe is the standard library for JSON encoding/decoding in our Scala stack.
import io.circe.*
import io.circe.generic.auto.*
import io.circe.syntax.*
case class Book(title: String, year: Int)
// Encoding: Scala → JSON
val json: Json = Book("Clean Code", 2008).asJson
// Decoding: JSON → Scala
val result: Either[Error, Book] = decode[Book]("""{"title":"Clean Code","year":2008}""")
Resources:
Orchescala – User
As a user, you use Orchescala to describe processes, domain objects, and workers – using the provided DSL. You don't need a deep understanding of the underlying libraries (ZIO, Tapir, etc.).
What You Do as a User
- Describe processes and sub-processes in the Orchescala DSL
- Model domain objects (Input/Output) as Case Classes
- Implement workers for Service Tasks
- Reference DMN decisions
Setup the Orchescala Projects
This section guides you through cloning all necessary repositories to start working on your Orchescala projects.
Git SSH Key Setup (Mac)
If you already have access rights to your company's Git provider (e.g. https://code.mycompany.com/repos), follow these steps to configure SSH-based access.
1. Generate an SSH key pair
# Generate a new Ed25519 key (recommended)
ssh-keygen -t ed25519 -C "your.email@company.com"
# Follow the prompts – choose a secure passphrase
# Default location: ~/.ssh/id_ed25519
2. Enable SSH passphrase caching via macOS Keychain
Add the following to your ~/.ssh/config (create the file if it doesn't exist):
Host *
UseKeychain yes
AddKeysToAgent yes
IdentityFile ~/.ssh/id_ed25519
Then add the key to the agent:
ssh-add --apple-use-keychain ~/.ssh/id_ed25519
💡 With UseKeychain yes, macOS stores the passphrase in the system Keychain – you only need to enter it once after each reboot.
3. Add the public key to your Git provider
# Copy the public key to your clipboard
pbcopy < ~/.ssh/id_ed25519.pub
Then paste it in your Git provider's SSH key settings (e.g. GitLab → Preferences → SSH Keys).
4. Verify the connection
# Replace with your Git provider's hostname
ssh -T git@code.mycompany.com
Project Checkout
This clones all required projects into your local projects folder.
dev-mycompany
├── mycompany-orchescala
├── projects
├── mycompany-commons
├── mycompany-services
└── mycompany-cards
1. Navigate to your development folder and download the checkout script:
cd ~/dev-mycompany # or your preferred workspace directory
Download project-checkout.scala and put it in your dev-mycompany folder.
2. Adjust the variables in the script to match your company's project layout:
baseUrlto your company's Git provider likehttps://code.mycompany.com/reposcompanyto your company name likemycompanyprojectsa list of all projects you want to clone likeSeq("mycompany-commons", "mycompany-services")
3. Make the script executable and run it:
chmod +x project-checkout.scala
./project-checkout.scala
Backend Development
Our backend is built on modern principles of distributed systems and cloud-native development.
Key Concepts
- REST / HTTP APIs – Communication between services
- Event-driven Architecture – Messaging via Kafka, etc.
- Domain-Driven Design (DDD) – Structuring business logic
- Hexagonal Architecture – Separation of domain and infrastructure
Recommended Reading
- 📖 Building Microservices – Sam Newman
- 📖 Domain-Driven Design Reference – Eric Evans
- 🎓 Microservices.io – Patterns & Best Practices
Open API
We use OpenAPI (formerly Swagger) for describing and documenting our REST APIs. API-first is our standard approach.
Concepts
- OpenAPI Specification (OAS 3.x) – YAML/JSON-based API description
- Code Generation – Generate client/server stubs from the spec (e.g. via Tapir)
- API-first – Spec is defined first, implementation follows
Tools (Mac)
# Swagger UI locally (via Docker)
docker run -p 8081:8080 swaggerapi/swagger-ui
# OpenAPI Generator CLI
brew install openapi-generator
# Example: Generate Scala client
openapi-generator generate -i openapi.yaml -g scala-sttp -o ./client
Further Reading
Functional Programming
Functional programming (FP) is a fundamental paradigm in our stack – especially in Scala. It's worth understanding the concepts before diving deep into the developer libraries.
Core Concepts
| Concept | Meaning |
|---|---|
| Pure Functions | No side effects, same input → same output |
| Immutability | Data is not modified, but recreated |
| Higher-Order Functions | Functions as parameters or return values |
| Typeclasses | Abstraction over types (Functor, Monad, etc.) |
| Monads | Structured chaining of computations (Option, Either, IO) |
| Referential Transparency | Expression can be replaced by its result |
Recommended Resources
- 📖 Scala with Cats (free) – FP concepts with Scala
- 🎓 Functional Programming in Scala (Coursera)
- 📖 Cats Documentation
Groovy
Groovy is primarily used for Camunda scripts and build scripts (Gradle). It is a dynamic JVM language with Scala/Java interoperability.
Installation (Mac)
# Via Homebrew
brew install groovy
# Check version
groovy --version
# Interactive shell
groovysh
Typical Usage in the Camunda Context
// Example: Set process variable in a Script Task
execution.setVariable("approved", true)
// Read variables
def amount = execution.getVariable("amount") as Double
// Decision
if (amount > 10000) {
execution.setVariable("requiresApproval", true)
}
Further Reading
Scala – Developer
As a developer of the Orchescala framework, you need next to the basic Scala 3 stuff from above, an understanding of the Effect libraries like ZIO and Scala 3 Macros.
Additional Setup (Mac)
# Scala CLI (already installed via `cs setup`, alternatively via Homebrew)
brew install Virtuslab/scala-cli/scala-cli
# List available JVMs
cs java --available
# Switch between JVM versions
cs java --jvm temurin:21 --setup # Temurin 21 as default
cs java --jvm temurin:17 --setup # Temurin 17 as default
Scala 3 Macros
Scala 3 Macros enable compile-time metaprogramming in Scala – code that inspects or generates other code during compilation, before the program ever runs.
This is fundamentally different from runtime reflection: errors are caught at compile-time, there is no runtime overhead, and the generated code is fully type-safe.
Key Macro Mechanisms:
| Mechanism | Purpose |
|---|---|
inline |
Forces inlining at call-site; prerequisite for most macros |
Expr[T] |
Typed representation of a code expression |
Quotes |
The compile-time context needed to inspect/build expressions |
scala.quoted.* |
The standard macro API |
How We Use It in Orchescala:
The most visible use case is automatic variable name extraction in Simulations. Instead of specifying field names as strings (fragile, not refactor-safe), we extract them directly from the variable reference at compile-time:
// Without macros – fragile string, breaks silently on rename:
variable("amount", myProcess.amount)
// With macros – name is extracted from the val reference itself:
variable(myProcess.amount) // name "amount" extracted at compile-time
Under the hood, a macro inspects the Expr of the argument, reads the symbol name from the AST, and injects it as a compile-time constant – no strings, no runtime cost.
Inline + Macro pattern (simplified):
import scala.quoted.*
inline def nameOf[T](inline value: T): String =
${ nameOfImpl('value) }
def nameOfImpl[T](expr: Expr[T])(using Quotes): Expr[String] =
import quotes.reflect.*
val name = expr.asTerm match
case Select(_, name) => name
case _ => report.errorAndAbort("Expected a field reference")
Expr(name)
💡 You don't need to write macros as an Orchescala user – but understanding the concept helps you make sense of why the DSL can "magically" know variable names.
Resources:
- 📖 Scala 3 Macros Guide
- 📖 Inline & Compile-time Ops
- 📖 Quoted Code –
ExprandQuotes - 🎓 Macro Tutorial by Adam Warski
ZIO
ZIO is our primary framework for asynchronous and functional effects. It replaces Future and enables type-safe, composable effect management.
# Add to build.sbt
libraryDependencies += "dev.zio" %% "zio" % "2.x.x"
Core Concepts:
ZIO[R, E, A]– Description of an effect (Environment, Error, Success)ZLayer– Dependency InjectionZStream– Streaming- Fiber-based concurrency
Resources:
Orchescala – Developer
As a developer of Orchescala itself, you work on the framework core: designing the DSL, extending library integrations, and maintaining the infrastructure.
What You Do as a Developer
- Design and implement Orchescala DSL constructs
- Build ZIO layers and services for new integrations
- Extend Tapir endpoints and OpenAPI generation
- Define Iron constraints for domain validation
- Implement Circe codecs for new data types
- Write framework tests and maintain CI/CD
Architecture Overview
- Built on Scala 3 + ZIO 2 + Tapir + Camunda REST API
- Defines conventions for process workers, API endpoints, and data models
- Integrates domain concepts into a type-safe Scala API
- Uses Iron for validated domain objects and Circe for JSON serialization
Getting Started
⚠️ Internal framework: Documentation and source code are available in the internal Git repository.
- Request repository access from the team lead
- Read the architecture documentation and Developer Guide (internal Confluence)
- Build the framework module locally and run tests
- Implement your first framework contribution via pair programming
Questions? Don't hesitate to ask the team – nobody expects you to learn everything at once! 🙌