Repository: serverlessworkflow/sdk-java
Pull request #646 (2025-07-18 23:44:54)

Introduce Java Fluent DSL
**Many thanks for submitting your Pull Request :heart:!**

**What this PR does / why we need it**:

**Special notes for reviewers**:

**Additional information (if needed):**


Patch:
diff --git a/experimental/lambda/pom.xml b/experimental/lambda/pom.xml
index 72e4c0b..5c20338 100644
--- a/experimental/lambda/pom.xml
+++ b/experimental/lambda/pom.xml
@@ -1,22 +1,27 @@
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
-  <modelVersion>4.0.0</modelVersion>
-  <parent>
-    <groupId>io.serverlessworkflow</groupId>
-    <artifactId>serverlessworkflow-experimental</artifactId>
-    <version>8.0.0-SNAPSHOT</version>
-  </parent>
-  <artifactId>serverlessworkflow-experimental-lambda</artifactId>
-  <name>ServelessWorkflow:: Experimental:: lambda</name>
-  <dependencies>
-    <dependency>
-      <groupId>io.serverlessworkflow</groupId>
-      <artifactId>serverlessworkflow-experimental-types</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>io.serverlessworkflow</groupId>
-      <artifactId>serverlessworkflow-impl-core</artifactId>
-    </dependency>
-    <dependency>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>io.serverlessworkflow</groupId>
+        <artifactId>serverlessworkflow-experimental</artifactId>
+        <version>8.0.0-SNAPSHOT</version>
+    </parent>
+    <artifactId>serverlessworkflow-experimental-lambda</artifactId>
+    <name>ServelessWorkflow:: Experimental:: lambda</name>
+    <dependencies>
+        <dependency>
+            <groupId>io.serverlessworkflow</groupId>
+            <artifactId>serverlessworkflow-experimental-types</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>io.serverlessworkflow</groupId>
+            <artifactId>serverlessworkflow-impl-core</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>io.serverlessworkflow</groupId>
+            <artifactId>serverlessworkflow-fluent-java</artifactId>
+        </dependency>
+        <dependency>
             <groupId>org.junit.jupiter</groupId>
             <artifactId>junit-jupiter-api</artifactId>
             <scope>test</scope>
@@ -41,5 +46,5 @@
             <artifactId>logback-classic</artifactId>
             <scope>test</scope>
         </dependency>
-  </dependencies>
+    </dependencies>
 </project>
\ No newline at end of file
diff --git a/experimental/pom.xml b/experimental/pom.xml
index 409e68c..e269b7b 100644
--- a/experimental/pom.xml
+++ b/experimental/pom.xml
@@ -30,6 +30,11 @@
             <artifactId>serverlessworkflow-experimental-types</artifactId>
             <version>${project.version}</version>
         </dependency>
+        <dependency>
+            <groupId>io.serverlessworkflow</groupId>
+            <artifactId>serverlessworkflow-fluent-java</artifactId>
+            <version>${project.version}</version>
+        </dependency>
     </dependencies>
   </dependencyManagement>
   <modules>
diff --git a/experimental/types/src/main/java/io/serverlessworkflow/api/types/SwitchCaseFunction.java b/experimental/types/src/main/java/io/serverlessworkflow/api/types/SwitchCaseFunction.java
index 027ab17..51b4175 100644
--- a/experimental/types/src/main/java/io/serverlessworkflow/api/types/SwitchCaseFunction.java
+++ b/experimental/types/src/main/java/io/serverlessworkflow/api/types/SwitchCaseFunction.java
@@ -27,6 +27,10 @@ public class SwitchCaseFunction extends SwitchCase {
     return this;
   }
 
+  public <T> void setPredicate(Predicate<T> predicate) {
+    this.predicate = predicate;
+  }
+
   public Predicate<?> predicate() {
     return predicate;
   }
diff --git a/fluent/java/pom.xml b/fluent/java/pom.xml
new file mode 100644
index 0000000..aa12ec9
--- /dev/null
+++ b/fluent/java/pom.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>io.serverlessworkflow</groupId>
+        <artifactId>serverlessworkflow-fluent</artifactId>
+        <version>8.0.0-SNAPSHOT</version>
+    </parent>
+
+    <name>Serverless Workflow :: Fluent :: Java</name>
+    <artifactId>serverlessworkflow-fluent-java</artifactId>
+
+    <properties>
+        <maven.compiler.source>17</maven.compiler.source>
+        <maven.compiler.target>17</maven.compiler.target>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>io.serverlessworkflow</groupId>
+            <artifactId>serverlessworkflow-experimental-types</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>io.serverlessworkflow</groupId>
+            <artifactId>serverlessworkflow-types</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>io.serverlessworkflow</groupId>
+            <artifactId>serverlessworkflow-fluent-standard</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.junit.jupiter</groupId>
+            <artifactId>junit-jupiter-api</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+</project>
\ No newline at end of file
diff --git a/fluent/java/src/main/java/io/serverlessworkflow/fluent/java/CallTaskJavaBuilder.java b/fluent/java/src/main/java/io/serverlessworkflow/fluent/java/CallTaskJavaBuilder.java
new file mode 100644
index 0000000..8149ebb
--- /dev/null
+++ b/fluent/java/src/main/java/io/serverlessworkflow/fluent/java/CallTaskJavaBuilder.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2020-Present The Serverless Workflow Specification Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.serverlessworkflow.fluent.java;
+
+import io.serverlessworkflow.api.types.CallJava;
+import io.serverlessworkflow.api.types.CallTaskJava;
+import io.serverlessworkflow.fluent.standard.TaskBaseBuilder;
+import java.util.function.Function;
+
+public class CallTaskJavaBuilder extends TaskBaseBuilder<CallTaskJavaBuilder>
+    implements JavaTransformationHandlers<CallTaskJavaBuilder> {
+
+  private CallTaskJava callTaskJava;
+
+  CallTaskJavaBuilder() {
+    callTaskJava = new CallTaskJava(new CallJava() {});
+    super.setTask(callTaskJava.getCallJava());
+  }
+
+  @Override
+  protected CallTaskJavaBuilder self() {
+    return this;
+  }
+
+  public <T, V> CallTaskJavaBuilder fn(Function<T, V> function) {
+    this.callTaskJava = new CallTaskJava(CallJava.function(function));
+    super.setTask(this.callTaskJava.getCallJava());
+    return this;
+  }
+
+  public CallTaskJava build() {
+    return this.callTaskJava;
+  }
+}
diff --git a/fluent/java/src/main/java/io/serverlessworkflow/fluent/java/DoTaskJavaBuilder.java b/fluent/java/src/main/java/io/serverlessworkflow/fluent/java/DoTaskJavaBuilder.java
new file mode 100644
index 0000000..b17cf64
--- /dev/null
+++ b/fluent/java/src/main/java/io/serverlessworkflow/fluent/java/DoTaskJavaBuilder.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2020-Present The Serverless Workflow Specification Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.serverlessworkflow.fluent.java;
+
+import io.serverlessworkflow.fluent.standard.BaseDoTaskBuilder;
+import java.util.function.Consumer;
+
+public class DoTaskJavaBuilder extends BaseDoTaskBuilder<DoTaskJavaBuilder, TaskItemListJavaBuilder>
+    implements JavaTransformationHandlers<DoTaskJavaBuilder> {
+
+  DoTaskJavaBuilder() {
+    super(new TaskItemListJavaBuilder());
+  }
+
+  @Override
+  protected DoTaskJavaBuilder self() {
+    return this;
+  }
+
+  public DoTaskJavaBuilder callFn(String name, Consumer<CallTaskJavaBuilder> consumer) {
+    this.innerListBuilder().callJava(name, consumer);
+    return this;
+  }
+
+  public DoTaskJavaBuilder callFn(Consumer<CallTaskJavaBuilder> consumer) {
+    this.innerListBuilder().callJava(consumer);
+    return this;
+  }
+
+  public DoTaskJavaBuilder forFn(String name, Consumer<ForTaskJavaBuilder> consumer) {
+    this.innerListBuilder().forFn(name, consumer);
+    return this;
+  }
+
+  public DoTaskJavaBuilder forFn(Consumer<ForTaskJavaBuilder> consumer) {
+    this.innerListBuilder().forFn(consumer);
+    return this;
+  }
+
+  public DoTaskJavaBuilder switchFn(String name, Consumer<SwitchTaskJavaBuilder> consumer) {
+    this.innerListBuilder().switchFn(name, consumer);
+    return this;
+  }
+
+  public DoTaskJavaBuilder switchFn(Consumer<SwitchTaskJavaBuilder> consumer) {
+    this.innerListBuilder().switchFn(consumer);
+    return this;
+  }
+}
diff --git a/fluent/java/src/main/java/io/serverlessworkflow/fluent/java/ForTaskJavaBuilder.java b/fluent/java/src/main/java/io/serverlessworkflow/fluent/java/ForTaskJavaBuilder.java
new file mode 100644
index 0000000..241272f
--- /dev/null
+++ b/fluent/java/src/main/java/io/serverlessworkflow/fluent/java/ForTaskJavaBuilder.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2020-Present The Serverless Workflow Specification Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.serverlessworkflow.fluent.java;
+
+import io.serverlessworkflow.api.types.CallJava;
+import io.serverlessworkflow.api.types.CallTaskJava;
+import io.serverlessworkflow.api.types.ForTaskConfiguration;
+import io.serverlessworkflow.api.types.ForTaskFunction;
+import io.serverlessworkflow.api.types.Task;
+import io.serverlessworkflow.api.types.TaskItem;
+import io.serverlessworkflow.fluent.standard.TaskBaseBuilder;
+import io.serverlessworkflow.impl.expressions.LoopFunction;
+import io.serverlessworkflow.impl.expressions.LoopPredicate;
+import io.serverlessworkflow.impl.expressions.LoopPredicateIndex;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.UUID;
+import java.util.function.Consumer;
+import java.util.function.Function;
+
+public class ForTaskJavaBuilder extends TaskBaseBuilder<ForTaskJavaBuilder>
+    implements JavaTransformationHandlers<ForTaskJavaBuilder> {
+
+  private final ForTaskFunction forTaskFunction;
+  private final List<TaskItem> items;
+
+  ForTaskJavaBuilder() {
+    this.forTaskFunction = new ForTaskFunction();
+    this.forTaskFunction.withFor(new ForTaskConfiguration());
+    this.items = new ArrayList<>();
+    super.setTask(forTaskFunction);
+  }
+
+  @Override
+  protected ForTaskJavaBuilder self() {
+    return this;
+  }
+
+  public <T, V> ForTaskJavaBuilder whileC(LoopPredicate<T, V> predicate) {
+    this.forTaskFunction.withWhile(predicate);
+    return this;
+  }
+
+  public <T, V> ForTaskJavaBuilder whileC(LoopPredicateIndex<T, V> predicate) {
+    this.forTaskFunction.withWhile(predicate);
+    return this;
+  }
+
+  public <T> ForTaskJavaBuilder collection(Function<T, Collection<?>> collectionF) {
+    this.forTaskFunction.withCollection(collectionF);
+    return this;
+  }
+
+  public <T, V, R> ForTaskJavaBuilder tasks(String name, LoopFunction<T, V, R> function) {
+    this.items.add(
+        new TaskItem(
+            name,
+            new Task()
+                .withCallTask(
+                    new CallTaskJava(
+                        CallJava.loopFunction(
+                            function, this.forTaskFunction.getFor().getEach())))));
+    return this;
+  }
+
+  public <T, V, R> ForTaskJavaBuilder tasks(LoopFunction<T, V, R> function) {
+    return this.tasks(UUID.randomUUID().toString(), function);
+  }
+
+  public ForTaskJavaBuilder tasks(Consumer<TaskItemListJavaBuilder> consumer) {
+    final TaskItemListJavaBuilder builder = new TaskItemListJavaBuilder();
+    consumer.accept(builder);
+    this.items.addAll(builder.build());
+    return this;
+  }
+
+  public ForTaskFunction build() {
+    this.forTaskFunction.setDo(this.items);
+    return this.forTaskFunction;
+  }
+}
diff --git a/fluent/java/src/main/java/io/serverlessworkflow/fluent/java/JavaTransformationHandlers.java b/fluent/java/src/main/java/io/serverlessworkflow/fluent/java/JavaTransformationHandlers.java
new file mode 100644
index 0000000..c8651b8
--- /dev/null
+++ b/fluent/java/src/main/java/io/serverlessworkflow/fluent/java/JavaTransformationHandlers.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2020-Present The Serverless Workflow Specification Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.serverlessworkflow.fluent.java;
+
+import io.serverlessworkflow.api.types.Export;
+import io.serverlessworkflow.api.types.ExportAsFunction;
+import io.serverlessworkflow.api.types.Input;
+import io.serverlessworkflow.api.types.InputFromFunction;
+import io.serverlessworkflow.api.types.Output;
+import io.serverlessworkflow.api.types.OutputAsFunction;
+import io.serverlessworkflow.fluent.standard.TransformationHandlers;
+import java.util.function.Function;
+
+public interface JavaTransformationHandlers<B extends JavaTransformationHandlers<B>>
+    extends TransformationHandlers {
+
+  default <T, V> B exportAsFn(Function<T, V> function) {
+    setExport(new Export().withAs(new ExportAsFunction().withFunction(function)));
+    return (B) this;
+  }
+
+  default <T, V> B inputFrom(Function<T, V> function) {
+    setInput(new Input().withFrom(new InputFromFunction().withFunction(function)));
+    return (B) this;
+  }
+
+  default <T, V> B outputAs(Function<T, V> function) {
+    setOutput(new Output().withAs(new OutputAsFunction().withFunction(function)));
+    return (B) this;
+  }
+}
diff --git a/fluent/java/src/main/java/io/serverlessworkflow/fluent/java/JavaWorkflowBuilder.java b/fluent/java/src/main/java/io/serverlessworkflow/fluent/java/JavaWorkflowBuilder.java
new file mode 100644
index 0000000..5d90b59
--- /dev/null
+++ b/fluent/java/src/main/java/io/serverlessworkflow/fluent/java/JavaWorkflowBuilder.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2020-Present The Serverless Workflow Specification Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.serverlessworkflow.fluent.java;
+
+import io.serverlessworkflow.fluent.standard.BaseWorkflowBuilder;
+import java.util.UUID;
+
+public class JavaWorkflowBuilder
+    extends BaseWorkflowBuilder<JavaWorkflowBuilder, DoTaskJavaBuilder, TaskItemListJavaBuilder>
+    implements JavaTransformationHandlers<JavaWorkflowBuilder> {
+
+  private JavaWorkflowBuilder(final String name, final String namespace, final String version) {
+    super(name, namespace, version);
+  }
+
+  public static JavaWorkflowBuilder workflow(final String name, final String namespace) {
+    return new JavaWorkflowBuilder(name, namespace, DEFAULT_VERSION);
+  }
+
+  public static JavaWorkflowBuilder workflow(final String name) {
+    return new JavaWorkflowBuilder(name, DEFAULT_NAMESPACE, DEFAULT_VERSION);
+  }
+
+  public static JavaWorkflowBuilder workflow() {
+    return new JavaWorkflowBuilder(
+        UUID.randomUUID().toString(), DEFAULT_NAMESPACE, DEFAULT_VERSION);
+  }
+
+  @Override
+  protected DoTaskJavaBuilder newDo() {
+    return new DoTaskJavaBuilder();
+  }
+
+  @Override
+  protected JavaWorkflowBuilder self() {
+    return this;
+  }
+}
diff --git a/fluent/java/src/main/java/io/serverlessworkflow/fluent/java/SwitchTaskJavaBuilder.java b/fluent/java/src/main/java/io/serverlessworkflow/fluent/java/SwitchTaskJavaBuilder.java
new file mode 100644
index 0000000..8b9d98e
--- /dev/null
+++ b/fluent/java/src/main/java/io/serverlessworkflow/fluent/java/SwitchTaskJavaBuilder.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2020-Present The Serverless Workflow Specification Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.serverlessworkflow.fluent.java;
+
+import io.serverlessworkflow.api.types.FlowDirective;
+import io.serverlessworkflow.api.types.FlowDirectiveEnum;
+import io.serverlessworkflow.api.types.SwitchCase;
+import io.serverlessworkflow.api.types.SwitchCaseFunction;
+import io.serverlessworkflow.api.types.SwitchItem;
+import io.serverlessworkflow.api.types.SwitchTask;
+import io.serverlessworkflow.fluent.standard.TaskBaseBuilder;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
+import java.util.function.Consumer;
+import java.util.function.Predicate;
+
+public class SwitchTaskJavaBuilder extends TaskBaseBuilder<SwitchTaskJavaBuilder>
+    implements JavaTransformationHandlers<SwitchTaskJavaBuilder> {
+
+  private final SwitchTask switchTask;
+  private final List<SwitchItem> switchItems;
+
+  SwitchTaskJavaBuilder() {
+    this.switchTask = new SwitchTask();
+    this.switchItems = new ArrayList<>();
+    super.setTask(switchTask);
+  }
+
+  @Override
+  protected SwitchTaskJavaBuilder self() {
+    return this;
+  }
+
+  public SwitchTaskJavaBuilder items(Consumer<SwitchCaseJavaBuilder> consumer) {
+    return this.items(UUID.randomUUID().toString(), consumer);
+  }
+
+  public SwitchTaskJavaBuilder items(String name, Consumer<SwitchCaseJavaBuilder> consumer) {
+    final SwitchCaseJavaBuilder switchCase = new SwitchCaseJavaBuilder();
+    consumer.accept(switchCase);
+    this.switchItems.add(new SwitchItem(name, switchCase.build()));
+    return this;
+  }
+
+  public SwitchTask build() {
+    this.switchTask.setSwitch(this.switchItems);
+    return switchTask;
+  }
+
+  public static final class SwitchCaseJavaBuilder {
+    private final SwitchCaseFunction switchCase;
+
+    SwitchCaseJavaBuilder() {
+      this.switchCase = new SwitchCaseFunction();
+    }
+
+    public <T> SwitchCaseJavaBuilder when(Predicate<T> when) {
+      this.switchCase.setPredicate(when);
+      return this;
+    }
+
+    public SwitchCaseJavaBuilder then(FlowDirective then) {
+      this.switchCase.setThen(then);
+      return this;
+    }
+
+    public SwitchCaseJavaBuilder then(FlowDirectiveEnum then) {
+      this.switchCase.setThen(new FlowDirective().withFlowDirectiveEnum(then));
+      return this;
+    }
+
+    public SwitchCase build() {
+      return this.switchCase;
+    }
+  }
+}
diff --git a/fluent/java/src/main/java/io/serverlessworkflow/fluent/java/TaskItemListJavaBuilder.java b/fluent/java/src/main/java/io/serverlessworkflow/fluent/java/TaskItemListJavaBuilder.java
new file mode 100644
index 0000000..40fa250
--- /dev/null
+++ b/fluent/java/src/main/java/io/serverlessworkflow/fluent/java/TaskItemListJavaBuilder.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2020-Present The Serverless Workflow Specification Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.serverlessworkflow.fluent.java;
+
+import io.serverlessworkflow.api.types.Task;
+import io.serverlessworkflow.api.types.TaskItem;
+import io.serverlessworkflow.fluent.standard.BaseTaskItemListBuilder;
+import java.util.UUID;
+import java.util.function.Consumer;
+
+public class TaskItemListJavaBuilder extends BaseTaskItemListBuilder<TaskItemListJavaBuilder> {
+
+  TaskItemListJavaBuilder() {
+    super();
+  }
+
+  @Override
+  protected TaskItemListJavaBuilder self() {
+    return this;
+  }
+
+  @Override
+  protected TaskItemListJavaBuilder newItemListBuilder() {
+    return new TaskItemListJavaBuilder();
+  }
+
+  public TaskItemListJavaBuilder callJava(String name, Consumer<CallTaskJavaBuilder> consumer) {
+    this.requireNameAndConfig(name, consumer);
+    final CallTaskJavaBuilder callTaskJavaBuilder = new CallTaskJavaBuilder();
+    consumer.accept(callTaskJavaBuilder);
+    return addTaskItem(new TaskItem(name, new Task().withCallTask(callTaskJavaBuilder.build())));
+  }
+
+  public TaskItemListJavaBuilder callJava(Consumer<CallTaskJavaBuilder> consumer) {
+    return this.callJava(UUID.randomUUID().toString(), consumer);
+  }
+
+  public TaskItemListJavaBuilder forFn(String name, Consumer<ForTaskJavaBuilder> consumer) {
+    this.requireNameAndConfig(name, consumer);
+    final ForTaskJavaBuilder forTaskJavaBuilder = new ForTaskJavaBuilder();
+    consumer.accept(forTaskJavaBuilder);
+    return this.addTaskItem(new TaskItem(name, new Task().withForTask(forTaskJavaBuilder.build())));
+  }
+
+  public TaskItemListJavaBuilder forFn(Consumer<ForTaskJavaBuilder> consumer) {
+    return this.forFn(UUID.randomUUID().toString(), consumer);
+  }
+
+  public TaskItemListJavaBuilder switchFn(String name, Consumer<SwitchTaskJavaBuilder> consumer) {
+    this.requireNameAndConfig(name, consumer);
+    final SwitchTaskJavaBuilder switchTaskJavaBuilder = new SwitchTaskJavaBuilder();
+    consumer.accept(switchTaskJavaBuilder);
+    return this.addTaskItem(
+        new TaskItem(name, new Task().withSwitchTask(switchTaskJavaBuilder.build())));
+  }
+
+  public TaskItemListJavaBuilder switchFn(Consumer<SwitchTaskJavaBuilder> consumer) {
+    return this.switchFn(UUID.randomUUID().toString(), consumer);
+  }
+}
diff --git a/fluent/pom.xml b/fluent/pom.xml
index 71d54a6..1af3056 100644
--- a/fluent/pom.xml
+++ b/fluent/pom.xml
@@ -25,11 +25,22 @@
                 <artifactId>serverlessworkflow-types</artifactId>
                 <version>${project.version}</version>
             </dependency>
+            <dependency>
+                <groupId>io.serverlessworkflow</groupId>
+                <artifactId>serverlessworkflow-experimental-types</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>io.serverlessworkflow</groupId>
+                <artifactId>serverlessworkflow-fluent-standard</artifactId>
+                <version>${project.version}</version>
+            </dependency>
         </dependencies>
     </dependencyManagement>
 
     <modules>
         <module>standard</module>
+        <module>java</module>
     </modules>
 
 </project>
\ No newline at end of file
diff --git a/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/BaseDoTaskBuilder.java b/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/BaseDoTaskBuilder.java
new file mode 100644
index 0000000..fab86cd
--- /dev/null
+++ b/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/BaseDoTaskBuilder.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright 2020-Present The Serverless Workflow Specification Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.serverlessworkflow.fluent.standard;
+
+import io.serverlessworkflow.api.types.DoTask;
+import java.util.function.Consumer;
+
+public abstract class BaseDoTaskBuilder<
+        TASK extends TaskBaseBuilder<TASK>, LIST extends BaseTaskItemListBuilder<LIST>>
+    extends TaskBaseBuilder<TASK> {
+  private final DoTask doTask;
+  private final BaseTaskItemListBuilder<LIST> taskItemListBuilder;
+
+  protected BaseDoTaskBuilder(BaseTaskItemListBuilder<LIST> taskItemListBuilder) {
+    this.doTask = new DoTask();
+    this.taskItemListBuilder = taskItemListBuilder;
+    this.setTask(doTask);
+  }
+
+  protected abstract TASK self();
+
+  protected LIST innerListBuilder() {
+    return (LIST) taskItemListBuilder;
+  }
+
+  public TASK set(String name, Consumer<SetTaskBuilder> itemsConfigurer) {
+    taskItemListBuilder.set(name, itemsConfigurer);
+    return self();
+  }
+
+  public TASK set(Consumer<SetTaskBuilder> itemsConfigurer) {
+    taskItemListBuilder.set(itemsConfigurer);
+    return self();
+  }
+
+  public TASK set(String name, final String expr) {
+    taskItemListBuilder.set(name, s -> s.expr(expr));
+    return self();
+  }
+
+  public TASK set(final String expr) {
+    taskItemListBuilder.set(expr);
+    return self();
+  }
+
+  public TASK forEach(String name, Consumer<ForTaskBuilder<LIST>> itemsConfigurer) {
+    taskItemListBuilder.forEach(name, itemsConfigurer);
+    return self();
+  }
+
+  public TASK forEach(Consumer<ForTaskBuilder<LIST>> itemsConfigurer) {
+    taskItemListBuilder.forEach(itemsConfigurer);
+    return self();
+  }
+
+  public TASK switchC(String name, Consumer<SwitchTaskBuilder> itemsConfigurer) {
+    taskItemListBuilder.switchC(name, itemsConfigurer);
+    return self();
+  }
+
+  public TASK switchC(Consumer<SwitchTaskBuilder> itemsConfigurer) {
+    taskItemListBuilder.switchC(itemsConfigurer);
+    return self();
+  }
+
+  public TASK raise(String name, Consumer<RaiseTaskBuilder> itemsConfigurer) {
+    taskItemListBuilder.raise(name, itemsConfigurer);
+    return self();
+  }
+
+  public TASK raise(Consumer<RaiseTaskBuilder> itemsConfigurer) {
+    taskItemListBuilder.raise(itemsConfigurer);
+    return self();
+  }
+
+  public TASK fork(String name, Consumer<ForkTaskBuilder> itemsConfigurer) {
+    taskItemListBuilder.fork(name, itemsConfigurer);
+    return self();
+  }
+
+  public TASK fork(Consumer<ForkTaskBuilder> itemsConfigurer) {
+    taskItemListBuilder.fork(itemsConfigurer);
+    return self();
+  }
+
+  public TASK listen(String name, Consumer<ListenTaskBuilder> itemsConfigurer) {
+    taskItemListBuilder.listen(name, itemsConfigurer);
+    return self();
+  }
+
+  public TASK listen(Consumer<ListenTaskBuilder> itemsConfigurer) {
+    taskItemListBuilder.listen(itemsConfigurer);
+    return self();
+  }
+
+  public TASK emit(String name, Consumer<EmitTaskBuilder> itemsConfigurer) {
+    taskItemListBuilder.emit(name, itemsConfigurer);
+    return self();
+  }
+
+  public TASK emit(Consumer<EmitTaskBuilder> itemsConfigurer) {
+    taskItemListBuilder.emit(itemsConfigurer);
+    return self();
+  }
+
+  public TASK tryC(String name, Consumer<TryTaskBuilder<LIST>> itemsConfigurer) {
+    taskItemListBuilder.tryC(name, itemsConfigurer);
+    return self();
+  }
+
+  public TASK tryC(Consumer<TryTaskBuilder<LIST>> itemsConfigurer) {
+    taskItemListBuilder.tryC(itemsConfigurer);
+    return self();
+  }
+
+  public TASK callHTTP(String name, Consumer<CallHTTPTaskBuilder> itemsConfigurer) {
+    taskItemListBuilder.callHTTP(name, itemsConfigurer);
+    return self();
+  }
+
+  public TASK callHTTP(Consumer<CallHTTPTaskBuilder> itemsConfigurer) {
+    taskItemListBuilder.callHTTP(itemsConfigurer);
+    return self();
+  }
+
+  public DoTask build() {
+    this.doTask.setDo(this.taskItemListBuilder.build());
+    return this.doTask;
+  }
+}
diff --git a/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/BaseTaskItemListBuilder.java b/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/BaseTaskItemListBuilder.java
new file mode 100644
index 0000000..566b493
--- /dev/null
+++ b/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/BaseTaskItemListBuilder.java
@@ -0,0 +1,176 @@
+/*
+ * Copyright 2020-Present The Serverless Workflow Specification Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.serverlessworkflow.fluent.standard;
+
+import io.serverlessworkflow.api.types.CallTask;
+import io.serverlessworkflow.api.types.Task;
+import io.serverlessworkflow.api.types.TaskBase;
+import io.serverlessworkflow.api.types.TaskItem;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+import java.util.UUID;
+import java.util.function.Consumer;
+
+/**
+ * A builder for an ordered {@link TaskItem} list.
+ *
+ * <p>This builder only knows how to append new TaskItems of various flavors, but does NOT expose
+ * {@link TaskBase}‑level methods like export(), input(), etc. Those belong on {@link
+ * TaskBaseBuilder} subclasses.
+ *
+ * @param <SELF> the concrete builder type
+ */
+public abstract class BaseTaskItemListBuilder<SELF extends BaseTaskItemListBuilder<SELF>> {
+
+  private final List<TaskItem> list;
+
+  public BaseTaskItemListBuilder() {
+    this.list = new ArrayList<>();
+  }
+
+  protected abstract SELF self();
+
+  protected abstract SELF newItemListBuilder();
+
+  protected SELF addTaskItem(TaskItem taskItem) {
+    Objects.requireNonNull(taskItem, "taskItem must not be null");
+    list.add(taskItem);
+    return self();
+  }
+
+  protected void requireNameAndConfig(String name, Consumer<?> cfg) {
+    Objects.requireNonNull(name, "Task name must not be null");
+    Objects.requireNonNull(cfg, "Configurer must not be null");
+  }
+
+  public SELF set(String name, Consumer<SetTaskBuilder> itemsConfigurer) {
+    requireNameAndConfig(name, itemsConfigurer);
+    final SetTaskBuilder setBuilder = new SetTaskBuilder();
+    itemsConfigurer.accept(setBuilder);
+    return addTaskItem(new TaskItem(name, new Task().withSetTask(setBuilder.build())));
+  }
+
+  public SELF set(Consumer<SetTaskBuilder> itemsConfigurer) {
+    return this.set(UUID.randomUUID().toString(), itemsConfigurer);
+  }
+
+  public SELF set(String name, final String expr) {
+    return this.set(name, s -> s.expr(expr));
+  }
+
+  public SELF set(final String expr) {
+    return this.set(UUID.randomUUID().toString(), s -> s.expr(expr));
+  }
+
+  public SELF forEach(String name, Consumer<ForTaskBuilder<SELF>> itemsConfigurer) {
+    requireNameAndConfig(name, itemsConfigurer);
+    final ForTaskBuilder<SELF> forBuilder = new ForTaskBuilder<>(newItemListBuilder());
+    itemsConfigurer.accept(forBuilder);
+    return addTaskItem(new TaskItem(name, new Task().withForTask(forBuilder.build())));
+  }
+
+  public SELF forEach(Consumer<ForTaskBuilder<SELF>> itemsConfigurer) {
+    return this.forEach(UUID.randomUUID().toString(), itemsConfigurer);
+  }
+
+  public SELF switchC(String name, Consumer<SwitchTaskBuilder> itemsConfigurer) {
+    requireNameAndConfig(name, itemsConfigurer);
+    final SwitchTaskBuilder switchBuilder = new SwitchTaskBuilder();
+    itemsConfigurer.accept(switchBuilder);
+    return addTaskItem(new TaskItem(name, new Task().withSwitchTask(switchBuilder.build())));
+  }
+
+  public SELF switchC(Consumer<SwitchTaskBuilder> itemsConfigurer) {
+    return this.switchC(UUID.randomUUID().toString(), itemsConfigurer);
+  }
+
+  public SELF raise(String name, Consumer<RaiseTaskBuilder> itemsConfigurer) {
+    requireNameAndConfig(name, itemsConfigurer);
+    final RaiseTaskBuilder raiseBuilder = new RaiseTaskBuilder();
+    itemsConfigurer.accept(raiseBuilder);
+    return addTaskItem(new TaskItem(name, new Task().withRaiseTask(raiseBuilder.build())));
+  }
+
+  public SELF raise(Consumer<RaiseTaskBuilder> itemsConfigurer) {
+    return this.raise(UUID.randomUUID().toString(), itemsConfigurer);
+  }
+
+  public SELF fork(String name, Consumer<ForkTaskBuilder> itemsConfigurer) {
+    requireNameAndConfig(name, itemsConfigurer);
+    final ForkTaskBuilder forkBuilder = new ForkTaskBuilder();
+    itemsConfigurer.accept(forkBuilder);
+    return addTaskItem(new TaskItem(name, new Task().withForkTask(forkBuilder.build())));
+  }
+
+  public SELF fork(Consumer<ForkTaskBuilder> itemsConfigurer) {
+    return this.fork(UUID.randomUUID().toString(), itemsConfigurer);
+  }
+
+  public SELF listen(String name, Consumer<ListenTaskBuilder> itemsConfigurer) {
+    requireNameAndConfig(name, itemsConfigurer);
+    final ListenTaskBuilder listenBuilder = new ListenTaskBuilder();
+    itemsConfigurer.accept(listenBuilder);
+    return addTaskItem(new TaskItem(name, new Task().withListenTask(listenBuilder.build())));
+  }
+
+  public SELF listen(Consumer<ListenTaskBuilder> itemsConfigurer) {
+    return this.listen(UUID.randomUUID().toString(), itemsConfigurer);
+  }
+
+  public SELF emit(String name, Consumer<EmitTaskBuilder> itemsConfigurer) {
+    requireNameAndConfig(name, itemsConfigurer);
+    final EmitTaskBuilder emitBuilder = new EmitTaskBuilder();
+    itemsConfigurer.accept(emitBuilder);
+    return addTaskItem(new TaskItem(name, new Task().withEmitTask(emitBuilder.build())));
+  }
+
+  public SELF emit(Consumer<EmitTaskBuilder> itemsConfigurer) {
+    return this.emit(UUID.randomUUID().toString(), itemsConfigurer);
+  }
+
+  public SELF tryC(String name, Consumer<TryTaskBuilder<SELF>> itemsConfigurer) {
+    requireNameAndConfig(name, itemsConfigurer);
+    final TryTaskBuilder<SELF> tryBuilder = new TryTaskBuilder<>(this.newItemListBuilder());
+    itemsConfigurer.accept(tryBuilder);
+    return addTaskItem(new TaskItem(name, new Task().withTryTask(tryBuilder.build())));
+  }
+
+  public SELF tryC(Consumer<TryTaskBuilder<SELF>> itemsConfigurer) {
+    return this.tryC(UUID.randomUUID().toString(), itemsConfigurer);
+  }
+
+  public SELF callHTTP(String name, Consumer<CallHTTPTaskBuilder> itemsConfigurer) {
+    requireNameAndConfig(name, itemsConfigurer);
+    final CallHTTPTaskBuilder callHTTPBuilder = new CallHTTPTaskBuilder();
+    itemsConfigurer.accept(callHTTPBuilder);
+    return addTaskItem(
+        new TaskItem(
+            name, new Task().withCallTask(new CallTask().withCallHTTP(callHTTPBuilder.build()))));
+  }
+
+  public SELF callHTTP(Consumer<CallHTTPTaskBuilder> itemsConfigurer) {
+    return this.callHTTP(UUID.randomUUID().toString(), itemsConfigurer);
+  }
+
+  /**
+   * @return an immutable snapshot of all {@link TaskItem}s added so far
+   */
+  public List<TaskItem> build() {
+    return Collections.unmodifiableList(list);
+  }
+}
diff --git a/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/BaseWorkflowBuilder.java b/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/BaseWorkflowBuilder.java
new file mode 100644
index 0000000..b93bdda
--- /dev/null
+++ b/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/BaseWorkflowBuilder.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2020-Present The Serverless Workflow Specification Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.serverlessworkflow.fluent.standard;
+
+import io.serverlessworkflow.api.types.Document;
+import io.serverlessworkflow.api.types.Export;
+import io.serverlessworkflow.api.types.Input;
+import io.serverlessworkflow.api.types.Output;
+import io.serverlessworkflow.api.types.Workflow;
+import java.util.function.Consumer;
+
+public abstract class BaseWorkflowBuilder<
+        SELF extends BaseWorkflowBuilder<SELF, DO, LIST>,
+        DO extends BaseDoTaskBuilder<DO, LIST>,
+        LIST extends BaseTaskItemListBuilder<LIST>>
+    implements TransformationHandlers {
+
+  public static final String DSL = "1.0.0";
+  public static final String DEFAULT_VERSION = "0.0.1";
+  public static final String DEFAULT_NAMESPACE = "org.acme";
+
+  private final Workflow workflow;
+  private final Document document;
+
+  protected BaseWorkflowBuilder(final String name, final String namespace, final String version) {
+    this.document = new Document();
+    this.document.setName(name);
+    this.document.setNamespace(namespace);
+    this.document.setVersion(version);
+    this.document.setDsl(DSL);
+    this.workflow = new Workflow();
+    this.workflow.setDocument(this.document);
+  }
+
+  protected abstract DO newDo();
+
+  protected abstract SELF self();
+
+  @Override
+  public void setOutput(Output output) {
+    this.workflow.setOutput(output);
+  }
+
+  @Override
+  public void setExport(Export export) {
+    // TODO: build another interface with only Output and Input
+    throw new UnsupportedOperationException(
+        "export() is not supported on the workflow root; only tasks may export");
+  }
+
+  @Override
+  public void setInput(Input input) {
+    this.workflow.setInput(input);
+  }
+
+  public SELF document(Consumer<DocumentBuilder> documentBuilderConsumer) {
+    final DocumentBuilder documentBuilder = new DocumentBuilder(this.document);
+    documentBuilderConsumer.accept(documentBuilder);
+    return self();
+  }
+
+  public SELF use(Consumer<UseBuilder> useBuilderConsumer) {
+    final UseBuilder builder = new UseBuilder();
+    useBuilderConsumer.accept(builder);
+    this.workflow.setUse(builder.build());
+    return self();
+  }
+
+  public SELF tasks(Consumer<DO> doTaskConsumer) {
+    final DO doTaskBuilder = newDo();
+    doTaskConsumer.accept(doTaskBuilder);
+    this.workflow.setDo(doTaskBuilder.build().getDo());
+    return self();
+  }
+
+  public SELF input(Consumer<InputBuilder> inputBuilderConsumer) {
+    final InputBuilder inputBuilder = new InputBuilder();
+    inputBuilderConsumer.accept(inputBuilder);
+    this.workflow.setInput(inputBuilder.build());
+    return self();
+  }
+
+  public SELF output(Consumer<OutputBuilder> outputBuilderConsumer) {
+    final OutputBuilder outputBuilder = new OutputBuilder();
+    outputBuilderConsumer.accept(outputBuilder);
+    this.workflow.setOutput(outputBuilder.build());
+    return self();
+  }
+
+  public Workflow build() {
+    return this.workflow;
+  }
+}
diff --git a/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/DoTaskBuilder.java b/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/DoTaskBuilder.java
index 3de5cfe..dee523b 100644
--- a/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/DoTaskBuilder.java
+++ b/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/DoTaskBuilder.java
@@ -15,142 +15,14 @@
  */
 package io.serverlessworkflow.fluent.standard;
 
-import io.serverlessworkflow.api.types.CallTask;
-import io.serverlessworkflow.api.types.DoTask;
-import io.serverlessworkflow.api.types.Task;
-import io.serverlessworkflow.api.types.TaskItem;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.UUID;
-import java.util.function.Consumer;
-
-public class DoTaskBuilder extends TaskBaseBuilder<DoTaskBuilder> {
-
-  private final DoTask doTask;
-  private final List<TaskItem> list;
+public class DoTaskBuilder extends BaseDoTaskBuilder<DoTaskBuilder, TaskItemListBuilder> {
 
   DoTaskBuilder() {
-    this.doTask = new DoTask();
-    this.list = new ArrayList<>();
-    this.setTask(doTask);
+    super(new TaskItemListBuilder());
   }
 
   @Override
   protected DoTaskBuilder self() {
     return this;
   }
-
-  public DoTaskBuilder set(String name, Consumer<SetTaskBuilder> itemsConfigurer) {
-    final SetTaskBuilder setBuilder = new SetTaskBuilder();
-    itemsConfigurer.accept(setBuilder);
-    this.list.add(new TaskItem(name, new Task().withSetTask(setBuilder.build())));
-    return this;
-  }
-
-  public DoTaskBuilder set(Consumer<SetTaskBuilder> itemsConfigurer) {
-    return this.set(UUID.randomUUID().toString(), itemsConfigurer);
-  }
-
-  public DoTaskBuilder set(String name, final String expr) {
-    return this.set(name, s -> s.expr(expr));
-  }
-
-  public DoTaskBuilder set(final String expr) {
-    return this.set(UUID.randomUUID().toString(), s -> s.expr(expr));
-  }
-
-  public DoTaskBuilder forEach(String name, Consumer<ForTaskBuilder> itemsConfigurer) {
-    final ForTaskBuilder forBuilder = new ForTaskBuilder();
-    itemsConfigurer.accept(forBuilder);
-    this.list.add(new TaskItem(name, new Task().withForTask(forBuilder.build())));
-    return this;
-  }
-
-  public DoTaskBuilder forEach(Consumer<ForTaskBuilder> itemsConfigurer) {
-    return this.forEach(UUID.randomUUID().toString(), itemsConfigurer);
-  }
-
-  public DoTaskBuilder switchTask(String name, Consumer<SwitchTaskBuilder> itemsConfigurer) {
-    final SwitchTaskBuilder switchBuilder = new SwitchTaskBuilder();
-    itemsConfigurer.accept(switchBuilder);
-    this.list.add(new TaskItem(name, new Task().withSwitchTask(switchBuilder.build())));
-    return this;
-  }
-
-  public DoTaskBuilder switchTask(Consumer<SwitchTaskBuilder> itemsConfigurer) {
-    return this.switchTask(UUID.randomUUID().toString(), itemsConfigurer);
-  }
-
-  public DoTaskBuilder raise(String name, Consumer<RaiseTaskBuilder> itemsConfigurer) {
-    final RaiseTaskBuilder raiseBuilder = new RaiseTaskBuilder();
-    itemsConfigurer.accept(raiseBuilder);
-    this.list.add(new TaskItem(name, new Task().withRaiseTask(raiseBuilder.build())));
-    return this;
-  }
-
-  public DoTaskBuilder raise(Consumer<RaiseTaskBuilder> itemsConfigurer) {
-    return this.raise(UUID.randomUUID().toString(), itemsConfigurer);
-  }
-
-  public DoTaskBuilder fork(String name, Consumer<ForkTaskBuilder> itemsConfigurer) {
-    final ForkTaskBuilder forkBuilder = new ForkTaskBuilder();
-    itemsConfigurer.accept(forkBuilder);
-    this.list.add(new TaskItem(name, new Task().withForkTask(forkBuilder.build())));
-    return this;
-  }
-
-  public DoTaskBuilder fork(Consumer<ForkTaskBuilder> itemsConfigurer) {
-    return this.fork(UUID.randomUUID().toString(), itemsConfigurer);
-  }
-
-  public DoTaskBuilder listen(String name, Consumer<ListenTaskBuilder> itemsConfigurer) {
-    final ListenTaskBuilder listenBuilder = new ListenTaskBuilder();
-    itemsConfigurer.accept(listenBuilder);
-    this.list.add(new TaskItem(name, new Task().withListenTask(listenBuilder.build())));
-    return this;
-  }
-
-  public DoTaskBuilder listen(Consumer<ListenTaskBuilder> itemsConfigurer) {
-    return this.listen(UUID.randomUUID().toString(), itemsConfigurer);
-  }
-
-  public DoTaskBuilder emit(String name, Consumer<EmitTaskBuilder> itemsConfigurer) {
-    final EmitTaskBuilder emitBuilder = new EmitTaskBuilder();
-    itemsConfigurer.accept(emitBuilder);
-    this.list.add(new TaskItem(name, new Task().withEmitTask(emitBuilder.build())));
-    return this;
-  }
-
-  public DoTaskBuilder emit(Consumer<EmitTaskBuilder> itemsConfigurer) {
-    return this.emit(UUID.randomUUID().toString(), itemsConfigurer);
-  }
-
-  public DoTaskBuilder tryTask(String name, Consumer<TryTaskBuilder> itemsConfigurer) {
-    final TryTaskBuilder tryBuilder = new TryTaskBuilder();
-    itemsConfigurer.accept(tryBuilder);
-    this.list.add(new TaskItem(name, new Task().withTryTask(tryBuilder.build())));
-    return this;
-  }
-
-  public DoTaskBuilder tryTask(Consumer<TryTaskBuilder> itemsConfigurer) {
-    return this.tryTask(UUID.randomUUID().toString(), itemsConfigurer);
-  }
-
-  public DoTaskBuilder callHTTP(String name, Consumer<CallHTTPTaskBuilder> itemsConfigurer) {
-    final CallHTTPTaskBuilder callHTTPBuilder = new CallHTTPTaskBuilder();
-    itemsConfigurer.accept(callHTTPBuilder);
-    this.list.add(
-        new TaskItem(
-            name, new Task().withCallTask(new CallTask().withCallHTTP(callHTTPBuilder.build()))));
-    return this;
-  }
-
-  public DoTaskBuilder callHTTP(Consumer<CallHTTPTaskBuilder> itemsConfigurer) {
-    return this.callHTTP(UUID.randomUUID().toString(), itemsConfigurer);
-  }
-
-  public DoTask build() {
-    this.doTask.setDo(this.list);
-    return this.doTask;
-  }
 }
diff --git a/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/ForTaskBuilder.java b/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/ForTaskBuilder.java
index e755eeb..ba0cec7 100644
--- a/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/ForTaskBuilder.java
+++ b/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/ForTaskBuilder.java
@@ -19,46 +19,49 @@ import io.serverlessworkflow.api.types.ForTask;
 import io.serverlessworkflow.api.types.ForTaskConfiguration;
 import java.util.function.Consumer;
 
-public class ForTaskBuilder extends TaskBaseBuilder<ForTaskBuilder> {
+public class ForTaskBuilder<T extends BaseTaskItemListBuilder<T>>
+    extends TaskBaseBuilder<ForTaskBuilder<T>> {
 
   private final ForTask forTask;
   private final ForTaskConfiguration forTaskConfiguration;
+  private final T taskItemListBuilder;
 
-  ForTaskBuilder() {
+  ForTaskBuilder(T taskItemListBuilder) {
     super();
     forTask = new ForTask();
     forTaskConfiguration = new ForTaskConfiguration();
+    this.taskItemListBuilder = taskItemListBuilder;
     super.setTask(forTask);
   }
 
-  protected ForTaskBuilder self() {
+  protected ForTaskBuilder<T> self() {
     return this;
   }
 
-  public ForTaskBuilder each(String each) {
+  public ForTaskBuilder<T> each(String each) {
     forTaskConfiguration.setEach(each);
     return this;
   }
 
-  public ForTaskBuilder in(String in) {
+  public ForTaskBuilder<T> in(String in) {
     this.forTaskConfiguration.setIn(in);
     return this;
   }
 
-  public ForTaskBuilder at(String at) {
+  public ForTaskBuilder<T> at(String at) {
     this.forTaskConfiguration.setAt(at);
     return this;
   }
 
-  public ForTaskBuilder whileCondition(final String expression) {
+  public ForTaskBuilder<T> whileC(final String expression) {
     this.forTask.setWhile(expression);
     return this;
   }
 
-  public ForTaskBuilder doTasks(Consumer<DoTaskBuilder> doBuilderConsumer) {
-    final DoTaskBuilder doTaskBuilder = new DoTaskBuilder();
-    doBuilderConsumer.accept(doTaskBuilder);
-    this.forTask.setDo(doTaskBuilder.build().getDo());
+  public ForTaskBuilder<T> tasks(Consumer<T> doBuilderConsumer) {
+    final T taskItemListBuilder = this.taskItemListBuilder.newItemListBuilder();
+    doBuilderConsumer.accept(taskItemListBuilder);
+    this.forTask.setDo(taskItemListBuilder.build());
     return this;
   }
 
diff --git a/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/SwitchTaskBuilder.java b/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/SwitchTaskBuilder.java
index 12e027e..fca04cd 100644
--- a/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/SwitchTaskBuilder.java
+++ b/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/SwitchTaskBuilder.java
@@ -15,11 +15,14 @@
  */
 package io.serverlessworkflow.fluent.standard;
 
+import io.serverlessworkflow.api.types.FlowDirective;
+import io.serverlessworkflow.api.types.FlowDirectiveEnum;
 import io.serverlessworkflow.api.types.SwitchCase;
 import io.serverlessworkflow.api.types.SwitchItem;
 import io.serverlessworkflow.api.types.SwitchTask;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.UUID;
 import java.util.function.Consumer;
 
 public class SwitchTaskBuilder extends TaskBaseBuilder<SwitchTaskBuilder> {
@@ -39,7 +42,11 @@ public class SwitchTaskBuilder extends TaskBaseBuilder<SwitchTaskBuilder> {
     return this;
   }
 
-  public SwitchTaskBuilder switchTask(
+  public SwitchTaskBuilder items(Consumer<SwitchCaseBuilder> switchCaseConsumer) {
+    return this.items(UUID.randomUUID().toString(), switchCaseConsumer);
+  }
+
+  public SwitchTaskBuilder items(
       final String name, Consumer<SwitchCaseBuilder> switchCaseConsumer) {
     final SwitchCaseBuilder switchCaseBuilder = new SwitchCaseBuilder();
     switchCaseConsumer.accept(switchCaseBuilder);
@@ -64,8 +71,13 @@ public class SwitchTaskBuilder extends TaskBaseBuilder<SwitchTaskBuilder> {
       return this;
     }
 
-    public SwitchCaseBuilder then(String then) {
-      this.switchCase.setWhen(then);
+    public SwitchCaseBuilder then(FlowDirective then) {
+      this.switchCase.setThen(then);
+      return this;
+    }
+
+    public SwitchCaseBuilder then(FlowDirectiveEnum then) {
+      this.switchCase.setThen(new FlowDirective().withFlowDirectiveEnum(then));
       return this;
     }
 
diff --git a/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/TaskBaseBuilder.java b/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/TaskBaseBuilder.java
index 31d30b3..e4b9a62 100644
--- a/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/TaskBaseBuilder.java
+++ b/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/TaskBaseBuilder.java
@@ -21,24 +21,42 @@ import io.serverlessworkflow.api.types.ExportAs;
 import io.serverlessworkflow.api.types.ExternalResource;
 import io.serverlessworkflow.api.types.FlowDirective;
 import io.serverlessworkflow.api.types.FlowDirectiveEnum;
+import io.serverlessworkflow.api.types.Input;
+import io.serverlessworkflow.api.types.Output;
 import io.serverlessworkflow.api.types.SchemaExternal;
 import io.serverlessworkflow.api.types.SchemaInline;
 import io.serverlessworkflow.api.types.SchemaUnion;
 import io.serverlessworkflow.api.types.TaskBase;
 import java.util.function.Consumer;
 
-public abstract class TaskBaseBuilder<T extends TaskBaseBuilder<T>> {
-  protected abstract T self();
-
+public abstract class TaskBaseBuilder<T extends TaskBaseBuilder<T>>
+    implements TransformationHandlers {
   private TaskBase task;
 
   protected TaskBaseBuilder() {}
 
+  protected abstract T self();
+
   protected void setTask(TaskBase task) {
     this.task = task;
   }
 
-  public T _if(String id) {
+  @Override
+  public void setInput(Input input) {
+    this.task.setInput(input);
+  }
+
+  @Override
+  public void setExport(Export export) {
+    this.task.setExport(export);
+  }
+
+  @Override
+  public void setOutput(Output output) {
+    this.task.setOutput(output);
+  }
+
+  public T ifClause(String id) {
     this.task.setIf(id);
     return self();
   }
diff --git a/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/TaskItemListBuilder.java b/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/TaskItemListBuilder.java
new file mode 100644
index 0000000..ebc3353
--- /dev/null
+++ b/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/TaskItemListBuilder.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2020-Present The Serverless Workflow Specification Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.serverlessworkflow.fluent.standard;
+
+public class TaskItemListBuilder extends BaseTaskItemListBuilder<TaskItemListBuilder> {
+
+  TaskItemListBuilder() {
+    super();
+  }
+
+  @Override
+  protected TaskItemListBuilder self() {
+    return this;
+  }
+
+  @Override
+  protected TaskItemListBuilder newItemListBuilder() {
+    return new TaskItemListBuilder();
+  }
+}
diff --git a/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/TransformationHandlers.java b/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/TransformationHandlers.java
new file mode 100644
index 0000000..298f2fb
--- /dev/null
+++ b/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/TransformationHandlers.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2020-Present The Serverless Workflow Specification Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.serverlessworkflow.fluent.standard;
+
+import io.serverlessworkflow.api.types.Export;
+import io.serverlessworkflow.api.types.Input;
+import io.serverlessworkflow.api.types.Output;
+
+public interface TransformationHandlers {
+
+  void setOutput(final Output output);
+
+  void setExport(final Export export);
+
+  void setInput(final Input input);
+}
diff --git a/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/TryTaskBuilder.java b/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/TryTaskBuilder.java
index 2e02250..677a36f 100644
--- a/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/TryTaskBuilder.java
+++ b/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/TryTaskBuilder.java
@@ -34,27 +34,30 @@ import io.serverlessworkflow.api.types.TryTask;
 import io.serverlessworkflow.api.types.TryTaskCatch;
 import java.util.function.Consumer;
 
-public class TryTaskBuilder extends TaskBaseBuilder<TryTaskBuilder> {
+public class TryTaskBuilder<T extends BaseTaskItemListBuilder<T>>
+    extends TaskBaseBuilder<TryTaskBuilder<T>> {
 
   private final TryTask tryTask;
+  private final T doTaskBuilderFactory;
 
-  TryTaskBuilder() {
+  TryTaskBuilder(T doTaskBuilderFactory) {
     this.tryTask = new TryTask();
+    this.doTaskBuilderFactory = doTaskBuilderFactory;
   }
 
   @Override
-  protected TryTaskBuilder self() {
+  protected TryTaskBuilder<T> self() {
     return this;
   }
 
-  public TryTaskBuilder tryHandler(Consumer<DoTaskBuilder> consumer) {
-    final DoTaskBuilder doTaskBuilder = new DoTaskBuilder();
-    consumer.accept(doTaskBuilder);
-    this.tryTask.setTry(doTaskBuilder.build().getDo());
+  public TryTaskBuilder<T> tryHandler(Consumer<T> consumer) {
+    final T taskItemListBuilder = this.doTaskBuilderFactory.newItemListBuilder();
+    consumer.accept(taskItemListBuilder);
+    this.tryTask.setTry(taskItemListBuilder.build());
     return this;
   }
 
-  public TryTaskBuilder catchHandler(Consumer<TryTaskCatchBuilder> consumer) {
+  public TryTaskBuilder<T> catchHandler(Consumer<TryTaskCatchBuilder> consumer) {
     final TryTaskCatchBuilder catchBuilder = new TryTaskCatchBuilder();
     consumer.accept(catchBuilder);
     this.tryTask.setCatch(catchBuilder.build());
diff --git a/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/WorkflowBuilder.java b/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/WorkflowBuilder.java
index 742b576..278e1df 100644
--- a/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/WorkflowBuilder.java
+++ b/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/WorkflowBuilder.java
@@ -15,28 +15,18 @@
  */
 package io.serverlessworkflow.fluent.standard;
 
-import io.serverlessworkflow.api.types.Document;
-import io.serverlessworkflow.api.types.Workflow;
 import java.util.UUID;
-import java.util.function.Consumer;
 
-public class WorkflowBuilder {
-
-  private static final String DSL = "1.0.0";
-  private static final String DEFAULT_VERSION = "0.0.1";
-  private static final String DEFAULT_NAMESPACE = "org.acme";
-
-  private final Workflow workflow;
-  private final Document document;
+public class WorkflowBuilder
+    extends BaseWorkflowBuilder<WorkflowBuilder, DoTaskBuilder, TaskItemListBuilder> {
 
   private WorkflowBuilder(final String name, final String namespace, final String version) {
-    this.document = new Document();
-    this.document.setName(name);
-    this.document.setNamespace(namespace);
-    this.document.setVersion(version);
-    this.document.setDsl(DSL);
-    this.workflow = new Workflow();
-    this.workflow.setDocument(this.document);
+    super(name, namespace, version);
+  }
+
+  @Override
+  protected DoTaskBuilder newDo() {
+    return new DoTaskBuilder();
   }
 
   public static WorkflowBuilder workflow(
@@ -56,41 +46,8 @@ public class WorkflowBuilder {
     return new WorkflowBuilder(UUID.randomUUID().toString(), DEFAULT_NAMESPACE, DEFAULT_VERSION);
   }
 
-  public WorkflowBuilder document(Consumer<DocumentBuilder> documentBuilderConsumer) {
-    final DocumentBuilder documentBuilder = new DocumentBuilder(this.document);
-    documentBuilderConsumer.accept(documentBuilder);
-    return this;
-  }
-
-  public WorkflowBuilder use(Consumer<UseBuilder> useBuilderConsumer) {
-    final UseBuilder builder = new UseBuilder();
-    useBuilderConsumer.accept(builder);
-    this.workflow.setUse(builder.build());
-    return this;
-  }
-
-  public WorkflowBuilder doTasks(Consumer<DoTaskBuilder> doTaskConsumer) {
-    final DoTaskBuilder doTaskBuilder = new DoTaskBuilder();
-    doTaskConsumer.accept(doTaskBuilder);
-    this.workflow.setDo(doTaskBuilder.build().getDo());
+  @Override
+  protected WorkflowBuilder self() {
     return this;
   }
-
-  public WorkflowBuilder input(Consumer<InputBuilder> inputBuilderConsumer) {
-    final InputBuilder inputBuilder = new InputBuilder();
-    inputBuilderConsumer.accept(inputBuilder);
-    this.workflow.setInput(inputBuilder.build());
-    return this;
-  }
-
-  public WorkflowBuilder output(Consumer<OutputBuilder> outputBuilderConsumer) {
-    final OutputBuilder outputBuilder = new OutputBuilder();
-    outputBuilderConsumer.accept(outputBuilder);
-    this.workflow.setOutput(outputBuilder.build());
-    return this;
-  }
-
-  public Workflow build() {
-    return this.workflow;
-  }
 }


Test patch:
diff --git a/experimental/lambda/src/test/java/io/serverless/workflow/impl/CallTest.java b/experimental/lambda/src/test/java/io/serverless/workflow/impl/CallTest.java
index 9118ec0..7d95410 100644
--- a/experimental/lambda/src/test/java/io/serverless/workflow/impl/CallTest.java
+++ b/experimental/lambda/src/test/java/io/serverless/workflow/impl/CallTest.java
@@ -80,7 +80,7 @@ class CallTest {
                           new Task()
                               .withForTask(
                                   new ForTaskFunction()
-                                      .withWhile(this::isEven)
+                                      .withWhile(CallTest::isEven)
                                       .withCollection(v -> (Collection) v)
                                       .withFor(forConfig)
                                       .withDo(
@@ -91,7 +91,7 @@ class CallTest {
                                                       .withCallTask(
                                                           new CallTaskJava(
                                                               CallJava.loopFunction(
-                                                                  this::sum,
+                                                                  CallTest::sum,
                                                                   forConfig.getEach()))))))))));
 
       assertThat(
@@ -124,7 +124,7 @@ class CallTest {
                                               new SwitchItem(
                                                   "odd",
                                                   new SwitchCaseFunction()
-                                                      .withPredicate(this::isOdd)
+                                                      .withPredicate(CallTest::isOdd)
                                                       .withThen(
                                                           new FlowDirective()
                                                               .withFlowDirectiveEnum(
@@ -132,7 +132,7 @@ class CallTest {
                       new TaskItem(
                           "java",
                           new Task()
-                              .withCallTask(new CallTaskJava(CallJava.function(this::zero))))));
+                              .withCallTask(new CallTaskJava(CallJava.function(CallTest::zero))))));
 
       WorkflowDefinition definition = app.workflowDefinition(workflow);
       assertThat(definition.instance(3).start().get().asNumber().orElseThrow()).isEqualTo(3);
@@ -140,19 +140,19 @@ class CallTest {
     }
   }
 
-  private boolean isEven(Object model, Integer number) {
+  public static boolean isEven(Object model, Integer number) {
     return !isOdd(number);
   }
 
-  private boolean isOdd(Integer number) {
+  public static boolean isOdd(Integer number) {
     return number % 2 != 0;
   }
 
-  private int zero(Integer value) {
+  public static int zero(Integer value) {
     return 0;
   }
 
-  private Integer sum(Object model, Integer item) {
+  public static Integer sum(Object model, Integer item) {
     return model instanceof Collection ? item : (Integer) model + item;
   }
 }
diff --git a/experimental/lambda/src/test/java/io/serverless/workflow/impl/FluentDSLCallTest.java b/experimental/lambda/src/test/java/io/serverless/workflow/impl/FluentDSLCallTest.java
new file mode 100644
index 0000000..dd4f9a2
--- /dev/null
+++ b/experimental/lambda/src/test/java/io/serverless/workflow/impl/FluentDSLCallTest.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2020-Present The Serverless Workflow Specification Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.serverless.workflow.impl;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import io.serverlessworkflow.api.types.FlowDirectiveEnum;
+import io.serverlessworkflow.api.types.Workflow;
+import io.serverlessworkflow.fluent.java.JavaWorkflowBuilder;
+import io.serverlessworkflow.impl.WorkflowApplication;
+import io.serverlessworkflow.impl.WorkflowDefinition;
+import java.util.Collection;
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+import org.junit.jupiter.api.Test;
+
+public class FluentDSLCallTest {
+
+  @Test
+  void testJavaFunction() throws InterruptedException, ExecutionException {
+    try (WorkflowApplication app = WorkflowApplication.builder().build()) {
+      final Workflow workflow =
+          JavaWorkflowBuilder.workflow("testJavaCall")
+              .tasks(tasks -> tasks.callFn(f -> f.fn(JavaFunctions::getName)))
+              .build();
+      assertThat(
+              app.workflowDefinition(workflow)
+                  .instance(new Person("Francisco", 33))
+                  .start()
+                  .get()
+                  .asText()
+                  .orElseThrow())
+          .isEqualTo("Francisco Javierito");
+    }
+  }
+
+  @Test
+  void testForLoop() throws InterruptedException, ExecutionException {
+    try (WorkflowApplication app = WorkflowApplication.builder().build()) {
+      Workflow workflow =
+          JavaWorkflowBuilder.workflow()
+              .tasks(
+                  t ->
+                      t.forFn(
+                          f ->
+                              f.whileC(CallTest::isEven)
+                                  .collection(v -> (Collection<?>) v)
+                                  .tasks(CallTest::sum)))
+              .build();
+
+      assertThat(
+              app.workflowDefinition(workflow)
+                  .instance(List.of(2, 4, 6))
+                  .start()
+                  .get()
+                  .asNumber()
+                  .orElseThrow())
+          .isEqualTo(12);
+    }
+  }
+
+  @Test
+  void testSwitch() throws InterruptedException, ExecutionException {
+    try (WorkflowApplication app = WorkflowApplication.builder().build()) {
+      Workflow workflow =
+          JavaWorkflowBuilder.workflow()
+              .tasks(
+                  tasks ->
+                      tasks
+                          .switchFn(
+                              switchOdd ->
+                                  switchOdd.items(
+                                      item ->
+                                          item.when(CallTest::isOdd).then(FlowDirectiveEnum.END)))
+                          .callFn(callJava -> callJava.fn(CallTest::zero)))
+              .build();
+
+      WorkflowDefinition definition = app.workflowDefinition(workflow);
+      assertThat(definition.instance(3).start().get().asNumber().orElseThrow()).isEqualTo(3);
+      assertThat(definition.instance(4).start().get().asNumber().orElseThrow()).isEqualTo(0);
+    }
+  }
+}
diff --git a/fluent/java/src/test/java/io/serverlessworkflow/fluent/java/JavaWorkflowBuilderTest.java b/fluent/java/src/test/java/io/serverlessworkflow/fluent/java/JavaWorkflowBuilderTest.java
new file mode 100644
index 0000000..ef037ca
--- /dev/null
+++ b/fluent/java/src/test/java/io/serverlessworkflow/fluent/java/JavaWorkflowBuilderTest.java
@@ -0,0 +1,274 @@
+/*
+ * Copyright 2020-Present The Serverless Workflow Specification Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.serverlessworkflow.fluent.java;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+import io.serverlessworkflow.api.types.*;
+import io.serverlessworkflow.fluent.standard.BaseWorkflowBuilder;
+// if you reuse anything
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicBoolean;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+
+/** Tests for JavaWorkflowBuilder + Java DSL extensions. */
+class JavaWorkflowBuilderTest {
+
+  @Test
+  @DisplayName("Default Java workflow has auto-generated name and default namespace/version")
+  void testDefaults() {
+    Workflow wf = JavaWorkflowBuilder.workflow().build();
+    assertNotNull(wf);
+    Document doc = wf.getDocument();
+    assertNotNull(doc);
+    assertEquals(BaseWorkflowBuilder.DEFAULT_NAMESPACE, doc.getNamespace());
+    assertEquals(BaseWorkflowBuilder.DEFAULT_VERSION, doc.getVersion());
+    assertEquals(BaseWorkflowBuilder.DSL, doc.getDsl());
+    assertNotNull(doc.getName());
+  }
+
+  @Test
+  @DisplayName("Spec style forE still works inside Java workflow")
+  void testSpecForEachInJavaWorkflow() {
+    Workflow wf =
+        JavaWorkflowBuilder.workflow("specLoopFlow")
+            .tasks(
+                d ->
+                    d.forEach(f -> f.each("pet").in("$.pets"))
+                        .set("markDone", s -> s.expr("$.done = true")))
+            .build();
+
+    List<TaskItem> items = wf.getDo();
+    assertEquals(2, items.size());
+
+    TaskItem loopItem = items.get(0);
+    assertNotNull(loopItem.getTask().getForTask(), "Spec ForTask should be present");
+
+    TaskItem setItem = items.get(1);
+    assertNotNull(setItem.getTask().getSetTask());
+    SetTask st = setItem.getTask().getSetTask();
+    assertEquals("$.done = true", st.getSet().getString());
+  }
+
+  @Test
+  @DisplayName("Java style forE with collection + whileC builds ForTaskFunction")
+  void testJavaForEach() {
+    Workflow wf =
+        JavaWorkflowBuilder.workflow("javaLoopFlow")
+            .tasks(
+                d ->
+                    d.forFn(
+                        j ->
+                            j.collection(ctx -> List.of("a", "b", "c"))
+                                .whileC((String val, Object ctx) -> !val.equals("c"))
+                                .tasks(
+                                    inner -> inner.set("loopFlag", s -> s.expr("$.flag = true")))))
+            .build();
+
+    List<TaskItem> items = wf.getDo();
+    assertEquals(1, items.size());
+
+    TaskItem loopItem = items.get(0);
+    Task task = loopItem.getTask();
+
+    assertNotNull(task.getForTask(), "Java ForTaskFunction should be present");
+
+    // Basic structural checks on nested do inside the function loop
+    ForTaskFunction fn = (ForTaskFunction) task.getForTask();
+    assertNotNull(fn.getDo(), "Nested 'do' list inside ForTaskFunction should be populated");
+    assertEquals(1, fn.getDo().size());
+    Task nested = fn.getDo().get(0).getTask();
+    assertNotNull(nested.getSetTask());
+  }
+
+  @Test
+  @DisplayName("Mixed spec and Java loops in one workflow")
+  void testMixedLoops() {
+    Workflow wf =
+        JavaWorkflowBuilder.workflow("mixed")
+            .tasks(
+                d ->
+                    d.forEach(f -> f.each("item").in("$.array")) // spec
+                        .forFn(j -> j.collection(ctx -> List.of(1, 2, 3))) // java
+                )
+            .build();
+
+    List<TaskItem> items = wf.getDo();
+    assertEquals(2, items.size());
+
+    Task specLoop = items.get(0).getTask();
+    Task javaLoop = items.get(1).getTask();
+
+    assertNotNull(specLoop.getForTask());
+    assertNotNull(javaLoop.getForTask());
+  }
+
+  @Test
+  @DisplayName("Java functional exportAsFn/inputFrom/outputAs set function wrappers (not literals)")
+  void testJavaFunctionalIO() {
+    AtomicBoolean exportCalled = new AtomicBoolean(false);
+    AtomicBoolean inputCalled = new AtomicBoolean(false);
+    AtomicBoolean outputCalled = new AtomicBoolean(false);
+
+    Workflow wf =
+        JavaWorkflowBuilder.workflow("fnIO")
+            .tasks(
+                d ->
+                    d.set("init", s -> s.expr("$.x = 1"))
+                        .forFn(
+                            j ->
+                                j.collection(
+                                        ctx -> {
+                                          inputCalled.set(true);
+                                          return List.of("x", "y");
+                                        })
+                                    .tasks(inner -> inner.set("calc", s -> s.expr("$.y = $.x + 1")))
+                                    .exportAsFn(
+                                        item -> {
+                                          exportCalled.set(true);
+                                          return Map.of("computed", 42);
+                                        })
+                                    .outputAs(
+                                        item -> {
+                                          outputCalled.set(true);
+                                          return Map.of("out", true);
+                                        })))
+            .build();
+
+    // Top-level 'do' structure
+    assertEquals(2, wf.getDo().size());
+
+    // Find nested forTaskFunction
+    Task forTaskFnHolder = wf.getDo().get(1).getTask();
+    ForTaskFunction fn = (ForTaskFunction) forTaskFnHolder.getForTask();
+    assertNotNull(fn);
+
+    // Inspect nested tasks inside the function loop
+    List<TaskItem> nested = fn.getDo();
+    assertEquals(1, nested.size());
+    TaskBase nestedTask = nested.get(0).getTask().getSetTask();
+    assertNotNull(nestedTask);
+
+    // Because functions are likely stored as opaque objects, we check that
+    // export / output structures exist and are not expression-based.
+    Export export = fn.getExport();
+    assertNotNull(export, "Export should be set via functional variant");
+    assertNull(
+        export.getAs() != null ? export.getAs().getString() : null,
+        "Export 'as' should not be a plain string when using function variant");
+
+    Output out = fn.getOutput();
+    // If functional output maps to an OutputAsFunction wrapper, adapt the checks:
+    if (out != null && out.getAs() != null) {
+      // Expect no literal string if function used
+      assertNull(out.getAs().getString(), "Output 'as' should not be a literal string");
+    }
+
+    // We can't *invoke* lambdas here (unless your runtime exposes them),
+    // but we verified structural placement. Flipping AtomicBooleans in creation lambdas
+    // (collection) at least shows one function executed during build (if it is executed now;
+    // if they are deferred, remove those assertions.)
+  }
+
+  @Test
+  @DisplayName("callJava task added and retains name + CallTask union")
+  void testCallJavaTask() {
+    Workflow wf =
+        JavaWorkflowBuilder.workflow("callJavaFlow")
+            .tasks(
+                d ->
+                    d.callFn(
+                        "invokeHandler",
+                        cj -> {
+                          // configure your CallTaskJavaBuilder here
+                          // e.g., cj.className("com.acme.Handler").arg("key", "value");
+                        }))
+            .build();
+
+    List<TaskItem> items = wf.getDo();
+    assertEquals(1, items.size());
+    TaskItem ti = items.get(0);
+
+    assertEquals("invokeHandler", ti.getName());
+    Task task = ti.getTask();
+    assertNotNull(task.getCallTask(), "CallTask should be present for callJava");
+    // Additional assertions if CallTaskJavaBuilder populates fields
+    // e.g., assertEquals("com.acme.Handler", task.getCallTask().getCallJava().getClassName());
+  }
+
+  @Test
+  @DisplayName("switchCaseFn (Java variant) coexists with spec tasks")
+  void testSwitchCaseJava() {
+    Workflow wf =
+        JavaWorkflowBuilder.workflow("switchJava")
+            .tasks(
+                d ->
+                    d.set("prepare", s -> s.expr("$.ready = true"))
+                        .switchC(
+                            sw -> {
+                              // configure Java switch builder (cases / predicates)
+                            }))
+            .build();
+
+    List<TaskItem> items = wf.getDo();
+    assertEquals(2, items.size());
+
+    Task specSet = items.get(0).getTask();
+    Task switchTask = items.get(1).getTask();
+
+    assertNotNull(specSet.getSetTask());
+    assertNotNull(switchTask.getSwitchTask(), "SwitchTask union should be present");
+  }
+
+  @Test
+  @DisplayName("Combined: spec set + java forE + callJava inside nested do")
+  void testCompositeScenario() {
+    Workflow wf =
+        JavaWorkflowBuilder.workflow("composite")
+            .tasks(
+                d ->
+                    d.set("init", s -> s.expr("$.val = 0"))
+                        .forFn(
+                            j ->
+                                j.collection(ctx -> List.of("a", "b"))
+                                    .tasks(
+                                        inner ->
+                                            inner
+                                                .callJava(
+                                                    cj -> {
+                                                      // customizing Java call
+                                                    })
+                                                .set("flag", s -> s.expr("$.flag = true")))))
+            .build();
+
+    assertEquals(2, wf.getDo().size());
+
+    Task loopHolder = wf.getDo().get(1).getTask();
+    ForTaskFunction fn = (ForTaskFunction) loopHolder.getForTask();
+    assertNotNull(fn);
+
+    List<TaskItem> nested = fn.getDo();
+    assertEquals(2, nested.size());
+
+    Task nestedCall = nested.get(0).getTask();
+    Task nestedSet = nested.get(1).getTask();
+
+    assertNotNull(nestedCall.getCallTask());
+    assertNotNull(nestedSet.getSetTask());
+  }
+}
diff --git a/fluent/standard/src/test/java/io/serverlessworkflow/fluent/standard/WorkflowBuilderTest.java b/fluent/standard/src/test/java/io/serverlessworkflow/fluent/standard/WorkflowBuilderTest.java
index e29a27b..cdf1c3b 100644
--- a/fluent/standard/src/test/java/io/serverlessworkflow/fluent/standard/WorkflowBuilderTest.java
+++ b/fluent/standard/src/test/java/io/serverlessworkflow/fluent/standard/WorkflowBuilderTest.java
@@ -99,7 +99,7 @@ public class WorkflowBuilderTest {
   void testDoTaskSetAndForEach() {
     Workflow wf =
         WorkflowBuilder.workflow("flowDo")
-            .doTasks(
+            .tasks(
                 d ->
                     d.set("initCtx", "$.foo = 'bar'")
                         .forEach("item", f -> f.each("item").at("$.list")))
@@ -124,11 +124,11 @@ public class WorkflowBuilderTest {
   void testDoTaskMultipleTypes() {
     Workflow wf =
         WorkflowBuilder.workflow("flowMixed")
-            .doTasks(
+            .tasks(
                 d ->
                     d.set("init", s -> s.expr("$.init = true"))
                         .forEach("items", f -> f.each("item").in("$.list"))
-                        .switchTask(
+                        .switchC(
                             "choice",
                             sw -> {
                               // no-op configuration
@@ -154,7 +154,7 @@ public class WorkflowBuilderTest {
     assertEquals("init", setItem.getName());
     assertNotNull(setItem.getTask().getSetTask(), "SetTask should be present");
 
-    // forEach task
+    // forE task
     TaskItem forItem = items.get(1);
     assertEquals("items", forItem.getName());
     assertNotNull(forItem.getTask().getForTask(), "ForTask should be present");
@@ -179,7 +179,7 @@ public class WorkflowBuilderTest {
   void testDoTaskListenOne() {
     Workflow wf =
         WorkflowBuilder.workflow("flowListen")
-            .doTasks(
+            .tasks(
                 d ->
                     d.listen(
                         "waitCheck",
@@ -205,7 +205,7 @@ public class WorkflowBuilderTest {
   void testDoTaskEmitEvent() {
     Workflow wf =
         WorkflowBuilder.workflow("flowEmit")
-            .doTasks(
+            .tasks(
                 d ->
                     d.emit(
                         "emitEvent",
@@ -256,9 +256,9 @@ public class WorkflowBuilderTest {
   void testDoTaskTryCatchWithRetry() {
     Workflow wf =
         WorkflowBuilder.workflow("flowTry")
-            .doTasks(
+            .tasks(
                 d ->
-                    d.tryTask(
+                    d.tryC(
                         "tryBlock",
                         t ->
                             t.tryHandler(tb -> tb.set("init", s -> s.expr("$.start = true")))
@@ -304,9 +304,9 @@ public class WorkflowBuilderTest {
   void testDoTaskTryCatchErrorsFiltering() {
     Workflow wf =
         WorkflowBuilder.workflow("flowCatch")
-            .doTasks(
+            .tasks(
                 d ->
-                    d.tryTask(
+                    d.tryC(
                         "tryBlock",
                         t ->
                             t.tryHandler(tb -> tb.set("foo", s -> s.expr("$.foo = 'bar'")))
@@ -402,7 +402,7 @@ public class WorkflowBuilderTest {
   void testDoTaskCallHTTPBasic() {
     Workflow wf =
         WorkflowBuilder.workflow("flowCallBasic")
-            .doTasks(
+            .tasks(
                 d ->
                     d.callHTTP(
                         "basicCall",
@@ -428,7 +428,7 @@ public class WorkflowBuilderTest {
   void testDoTaskCallHTTPHeadersConsumerAndMap() {
     Workflow wf =
         WorkflowBuilder.workflow("flowCallHeaders")
-            .doTasks(
+            .tasks(
                 d ->
                     d.callHTTP(
                         "hdrCall",
@@ -444,7 +444,7 @@ public class WorkflowBuilderTest {
 
     Workflow wf2 =
         WorkflowBuilder.workflow()
-            .doTasks(
+            .tasks(
                 d ->
                     d.callHTTP(
                         c ->
@@ -460,7 +460,7 @@ public class WorkflowBuilderTest {
   void testDoTaskCallHTTPQueryConsumerAndMap() {
     Workflow wf =
         WorkflowBuilder.workflow("flowCallQuery")
-            .doTasks(
+            .tasks(
                 d ->
                     d.callHTTP(
                         "qryCall",
@@ -476,7 +476,7 @@ public class WorkflowBuilderTest {
 
     Workflow wf2 =
         WorkflowBuilder.workflow()
-            .doTasks(
+            .tasks(
                 d ->
                     d.callHTTP(
                         c -> c.method("GET").endpoint("uri").query(Map.of("q1", "x", "q2", "y"))))
@@ -498,7 +498,7 @@ public class WorkflowBuilderTest {
   void testDoTaskCallHTTPRedirectAndOutput() {
     Workflow wf =
         WorkflowBuilder.workflow("flowCallOpts")
-            .doTasks(
+            .tasks(
                 d ->
                     d.callHTTP(
                         "optCall",