@@ -53,6 +53,7 @@ Those examples contain the following workflow patterns:
53
53
4 . [ External Event Pattern] ( #external-event-pattern )
54
54
5 . [ Child-workflow Pattern] ( #child-workflow-pattern )
55
55
6 . [ Compensation Pattern] ( #compensation-pattern )
56
+ 7 . [ Cross-App Pattern] ( #cross-app-pattern )
56
57
57
58
### Chaining Pattern
58
59
In the chaining pattern, a sequence of activities executes in a specific order.
@@ -681,6 +682,158 @@ Key Points:
681
682
4 . Each activity simulates work with a short delay for demonstration purposes
682
683
683
684
685
+ ### Cross-App Pattern
686
+
687
+ The cross-app pattern allows workflows to call activities that are hosted in different Dapr applications. This is useful for microservices architectures allowing multiple applications to host activities that can be orchestrated by Dapr Workflows.
688
+
689
+ The ` CrossAppWorkflow ` class defines the workflow. It demonstrates calling activities in different apps using the ` appId ` parameter in ` WorkflowTaskOptions ` . See the code snippet below:
690
+ ``` java
691
+ public class CrossAppWorkflow implements Workflow {
692
+ @Override
693
+ public WorkflowStub create () {
694
+ return ctx - > {
695
+ var logger = ctx. getLogger();
696
+ logger. info(" === WORKFLOW STARTING ===" );
697
+ logger. info(" Starting CrossAppWorkflow: {}" , ctx. getName());
698
+ logger. info(" Workflow name: {}" , ctx. getName());
699
+ logger. info(" Workflow instance ID: {}" , ctx. getInstanceId());
700
+
701
+ String input = ctx. getInput(String . class);
702
+ logger. info(" CrossAppWorkflow received input: {}" , input);
703
+ logger. info(" Workflow input: {}" , input);
704
+
705
+ // Call an activity in another app by passing in an active appID to the WorkflowTaskOptions
706
+ logger. info(" Calling cross-app activity in 'app2'..." );
707
+ logger. info(" About to call cross-app activity in app2..." );
708
+ String crossAppResult = ctx. callActivity(
709
+ App2TransformActivity . class. getName(),
710
+ input,
711
+ new WorkflowTaskOptions (" app2" ),
712
+ String . class
713
+ ). await();
714
+
715
+ // Call another activity in a different app
716
+ logger. info(" Calling cross-app activity in 'app3'..." );
717
+ logger. info(" About to call cross-app activity in app3..." );
718
+ String finalResult = ctx. callActivity(
719
+ App3FinalizeActivity . class. getName(),
720
+ crossAppResult,
721
+ new WorkflowTaskOptions (" app3" ),
722
+ String . class
723
+ ). await();
724
+ logger. info(" Final cross-app activity result: {}" , finalResult);
725
+ logger. info(" Final cross-app activity result: {}" , finalResult);
726
+
727
+ logger. info(" CrossAppWorkflow finished with: {}" , finalResult);
728
+ logger. info(" === WORKFLOW COMPLETING WITH: {} ===" , finalResult);
729
+ ctx. complete(finalResult);
730
+ };
731
+ }
732
+ }
733
+
734
+ ```
735
+
736
+ The ` App2TransformActivity ` class defines an activity in app2 that transforms the input string. See the code snippet below:
737
+ ``` java
738
+ public class App2TransformActivity implements WorkflowActivity {
739
+ @Override
740
+ public Object run (WorkflowActivityContext ctx ) {
741
+ var logger = ctx. getLogger();
742
+ logger. info(" === App2: TransformActivity called ===" );
743
+ String input = ctx. getInput(String . class);
744
+ logger. info(" Input: {}" , input);
745
+
746
+ // Transform the input
747
+ String result = input. toUpperCase() + " [TRANSFORMED BY APP2]" ;
748
+
749
+ logger. info(" Output: {}" , result);
750
+ return result;
751
+ }
752
+ }
753
+ ```
754
+
755
+ The ` App3FinalizeActivity ` class defines an activity in app3 that finalizes the processing. See the code snippet below:
756
+ ``` java
757
+ public class App3FinalizeActivity implements WorkflowActivity {
758
+ @Override
759
+ public Object run (WorkflowActivityContext ctx ) {
760
+ var logger = ctx. getLogger();
761
+ logger. info(" === App3: FinalizeActivity called ===" );
762
+ String input = ctx. getInput(String . class);
763
+ logger. info(" Input: " , input);
764
+
765
+ // Finalize the processing
766
+ String result = input + " [FINALIZED BY APP3]" ;
767
+
768
+ logger. info(" Output: {}" , result);
769
+ return result;
770
+ }
771
+ }
772
+ ```
773
+
774
+ ** Key Features:**
775
+ - ** Cross-app activity calls** : Call activities in different Dapr applications specifying the appID in the WorkflowTaskOptions
776
+ - ** WorkflowTaskOptions with appId** : Specify which app should handle the activity
777
+ - ** Combined with retry policies** : Use app ID along with retry policies and handlers
778
+ - ** Error handling** : Works the same as local activity calls
779
+
780
+ ** Requirements:**
781
+ - Multiple Dapr applications running with different app IDs
782
+ - Activities registered in the target applications
783
+ - Proper Dapr workflow runtime configuration
784
+
785
+ ** Important Limitations:**
786
+ - ** Cross-app calls are currently supported for activities only**
787
+ - ** Child workflow cross-app calls (suborchestration) are NOT supported**
788
+ - The app ID must match the Dapr application ID of the target service
789
+
790
+ ** Running the Cross-App Example:**
791
+
792
+ This example requires running multiple Dapr applications simultaneously. You'll need to run the following commands in separate terminals:
793
+
794
+ 1 . ** Start the main workflow worker (crossapp-worker):**
795
+ ``` sh
796
+ dapr run --app-id crossapp-worker --resources-path ./components/workflows --dapr-grpc-port 50001 -- java -jar target/dapr-java-sdk-examples-exec.jar io.dapr.examples.workflows.crossapp.CrossAppWorker
797
+ ```
798
+
799
+ 2 . ** Start app2 worker (handles App2TransformActivity):**
800
+ ``` sh
801
+ dapr run --app-id app2 --resources-path ./components/workflows --dapr-grpc-port 50002 -- java -jar target/dapr-java-sdk-examples-exec.jar io.dapr.examples.workflows.crossapp.App2Worker
802
+ ```
803
+
804
+ 3 . ** Start app3 worker (handles App3FinalizeActivity):**
805
+ ``` sh
806
+ dapr run --app-id app3 --resources-path ./components/workflows --dapr-grpc-port 50003 -- java -jar target/dapr-java-sdk-examples-exec.jar io.dapr.examples.workflows.crossapp.App3Worker
807
+ ```
808
+
809
+ 4 . ** Run the workflow client:**
810
+ ``` sh
811
+ java -Djava.util.logging.ConsoleHandler.level=FINE -Dio.dapr.durabletask.level=FINE -jar target/dapr-java-sdk-examples-exec.jar io.dapr.examples.workflows.crossapp.CrossAppWorkflowClient " Hello World"
812
+ ```
813
+
814
+ ** Expected Output:**
815
+
816
+ The client will show:
817
+ ``` text
818
+ === Starting Cross-App Workflow Client ===
819
+ Input: Hello World
820
+ Created DaprWorkflowClient successfully
821
+ Attempting to start new workflow...
822
+ Started a new cross-app workflow with instance ID: 001113f3-b9d9-438c-932a-a9a9b70b9460
823
+ Waiting for workflow completion...
824
+ Workflow instance with ID: 001113f3-b9d9-438c-932a-a9a9b70b9460 completed with result: HELLO WORLD [TRANSFORMED BY APP2] [FINALIZED BY APP3]
825
+ ```
826
+
827
+ The workflow demonstrates:
828
+ 1 . The workflow starts in the main worker (crossapp-worker)
829
+ 2 . Calls an activity in 'app2' using cross-app functionality
830
+ 3 . Calls an activity in 'app3' using cross-app functionality
831
+ 4 . The workflow completes with the final result from all activities
832
+
833
+ This pattern is particularly useful for:
834
+ - Microservices architectures where activities are distributed across multiple services
835
+ - Multi-tenant applications where activities are isolated by app ID
836
+
684
837
### Suspend/Resume Pattern
685
838
686
839
Workflow instances can be suspended and resumed. This example shows how to use the suspend and resume commands.
0 commit comments