From 9de94eb01ae56618c1292ecd533fddff9050d8cc Mon Sep 17 00:00:00 2001 From: Cassandra Coyle Date: Fri, 19 Sep 2025 17:58:21 -0500 Subject: [PATCH 1/5] initial apps and architecture Signed-off-by: Cassandra Coyle --- workflows/multiapp/E-CommerceArchitecture.png | Bin 0 -> 87534 bytes workflows/multiapp/components/statestore.yaml | 16 ++ .../multiapp/go/order-orchestrator/go.mod | 30 +++ .../multiapp/go/order-orchestrator/go.sum | 62 +++++ .../multiapp/go/order-orchestrator/main.go | 218 ++++++++++++++++++ .../multiapp/go/order-orchestrator/models.go | 66 ++++++ workflows/multiapp/java/.sdkmanrc | 4 + .../multiapp/java/payment-service/pom.xml | 113 +++++++++ .../workflows/PaymentServiceApplication.java | 51 ++++ .../ValidatePaymentMethodActivity.java | 34 +++ workflows/multiapp/makefile | 84 +++++++ 11 files changed, 678 insertions(+) create mode 100644 workflows/multiapp/E-CommerceArchitecture.png create mode 100644 workflows/multiapp/components/statestore.yaml create mode 100644 workflows/multiapp/go/order-orchestrator/go.mod create mode 100644 workflows/multiapp/go/order-orchestrator/go.sum create mode 100644 workflows/multiapp/go/order-orchestrator/main.go create mode 100644 workflows/multiapp/go/order-orchestrator/models.go create mode 100644 workflows/multiapp/java/.sdkmanrc create mode 100644 workflows/multiapp/java/payment-service/pom.xml create mode 100644 workflows/multiapp/java/payment-service/src/main/java/io/dapr/quickstarts/workflows/PaymentServiceApplication.java create mode 100644 workflows/multiapp/java/payment-service/src/main/java/io/dapr/quickstarts/workflows/activities/ValidatePaymentMethodActivity.java create mode 100644 workflows/multiapp/makefile diff --git a/workflows/multiapp/E-CommerceArchitecture.png b/workflows/multiapp/E-CommerceArchitecture.png new file mode 100644 index 0000000000000000000000000000000000000000..b798e908c18228946e044f9188cf26a4ec8939c7 GIT binary patch literal 87534 zcmdSBWmp}}wk=GM;1+_ryIXK~cXxMKxCc){aCd?R3+@&yxLa^{U%21SKKsc1PTu?f zgJ(Ufsjlj(nq^~-IhxPPic-i3cnA;>5Xdsp;;Il3&;}3?kgjlVfs~avdnO2oH!3z_ zV#+dNVkF8SCrcZ93kV3DB=^{MIZ+jyUxCT!{wmsLLFQt?63bs~IX)xc^?ng7`k^%~ zd;+S^>lEG#C$V+3wRVjRCrL+)%oWPjsrt5kLsT)72Q}^;z|Ws~2^Pg@v7yQoW=pCT zy{tXGj#BVEqIbBO8YRhUiQ{_iT8~%1yZNO?x2_x@l((9GacmXCzo2!NaO5aF?C^ZT zop~e8;qWQ&Q#kVU&_^@r4_>jXn34On<}@3bo7zd^?<30mbPmcxA-lP|1tj%j;Dq2F zyWPFZqZ2+0Y}vi#^ta-uZzsvDNis(GPKBmITRA9pa4bSw_mTnRs zcmia<)W>OkEqXuMuy)yCAQ-W0)x_ivx!`OH<%SR*0@zs7LR-dCK>>mexQ2sx6JY}Z z4P3neK6ty8g*1q9!II1AMEQfh;T>U9Fwm z#$51Dfk(~TsB61vE6DSjIXN(ym^+zTFnKvR|0x2&@5KvTI#{@wka#)RJG%0E36T9O z2QP5_Cz+Xy6b>;%ZP6_iQDoIn;NTudxXEM$TRBqSvKAahGzRdLCG7YF_lAhULJ zbLM4c_Vn~*@?>Xn0$DM$^6>C5v#>FbjE zud;0cM|6P!u`A^sXhbjKU&i_gU3@wPj&-`D5CWwGNw8jkqAq*iSE~4)B z<~R%X<9ms@eqsi>F&`<8HeIEU+VardZ<2A}YbM z@ksHM#gu;6Wjo02?d-Met+l}KS(knBA;RmA)6$1|-^w8eS2C*bkEF(MIFNt+f{|P= z+c1*LS@rkl-$5kEkod=(K=D|_H{=E&5mRcUH)220SfKy%Me_ktvgJL2SY+4V?*oN` z^x<$0;t}8Rnfz0u!iBrgS*8ltadeUX+US2JZj1huPSKD34gXI~VSz@7{usvu(?4PQ z*QWj}kpvrP?{;vn;y>62s7l!x`l}o5hj6Tre>P(?8p{+VHibv`KleJA+_*rli?t5t zFIM|^`v$^*_BNALWBhZk-xB~sfDrwy@1Ms>mI4bwpGont2j!nDl12uy6ugy+ z?EB{tC-~Fr|KAy=y0;&ERM|OHQk+f4z3OH5qlW)}JK<$<;+vSO0b&jgpGUdh?TY8d zG(6$!&5{%7X2D8sJX@eJ@bv|}>U&1%X2deV*tkqqYc-CNJ#_mLK^(AuDH1Tod&H3T z!Ns&=%jfm^cBi6ob?ElF+HtwYB2qq;g>-HwbgWXlTGVnZGmZSX^{j*5b#H9cW$f=m z5HAL-{q1wQ`fu2bx)g|546#-@{+c+Uusa=(rwP|b^TXSN2_eP#r=3sWVK%Sx_3!hW zS($?VEmsHAQt2EvWHACasRYhH-+*Q{v?vMZ_i_T60-vw7+6vya+zioI4)kFgF+4w9 zU*6lSwl!a!EOC<}fc+Psd{!a*LJmUb{0JudhtuPAtLZlpdC>#)=yv((_=ib;6#iP!y5 z`ed=5eEzik){r>Ng;zv2B_5H`fyfIJ_?VM%p`;lRp2U?05m`fnm6#gpBC`B zianeyd)#sz4=#V>2YxV8 zsd~v~ZI$`pyFdjOGUmjr^Ij!_nIPFDI&TNcn~)l>3sYi`y(~|Z>>C&LGR@KA{#u)f zT#@J_kH745!a5?t{dT&B({3sh_DAR+p3ZxAwqk^$b6M?wvux-qzdT(kEx3*gSp59> z2K3t`!aISa9pwPjPw08qLmY4?63q{m4)h0RVai;Ep6>}JGXAn+7OzIMz|HS<+CZeB zAb4aKlilfhJFQ9=QDNzijw>zjF0N-b#aX*DL{Z!Q#DR%ejJgB&dpXR6d>)DPKbl0D z;qQAxODu+yL8F}W;$ugOUGyXNenQyg1!i}v5b6Q= z7rxUz<>wTwk@BC9b9zomrz@?9i~Ixd)R`nw#Qt>*dJXcx0u$mnC`fqnuKKW(sK&2y zvl+?k(|!1ree7J94+<6~akbs|(RX(=WACho;%Jz;^Xl>S`7Ds!#`zX}%1%RLCj zf`)v9L1iHLuum@Zv;S%P@vJk2$91>&iaz)N!5pFhGUSO=ELZYi2E;(XDRbWfh(^h(r9qim7OoThx3(Z!{e?hK{Fyy``w7lS6)Hn0lp;mD1vv! zU495r*v=us-Qu79N4|6iH=q`vh$B*|hP*}og2o>zk@1=L<2ZizOqrI_&#vD8y~hXD z{J|C$?QcVeV&)(NG;ls{^;?XjvKoq!;T$^!K4x&(|5kQv+IZW1+H%x(fR;`(Szt3= z1e#Hix|d6B6ON~`d508}hT1Eve5E~Gd-K~2wX5;V5U>)rK1YIfhVIo;#YKck(2!y| zkzSZPpFqvvA$ozXZ;SY$LB`!zv<$udO4IX41PMw>>(6)gLHojy;ybS|XRq5=(@OJV zF=Dlrqfah-V_EGo7~<-N-hIvIFxdL*)!A$ovg)Q3@dG!6bq4RT>w;J&ANqw}Hnu*Z zeYtMyzHlywjuVVvALpOO;I;nwJy)S%-g|X2yE?qS;x9PB0w!o#k#b0Oup*Nc{bwjN zBSPkpscN2}$kp3AY5==Kk*JbbtFue?>|?p&6F#}31d^sAhQSDtBAv2IsczuB`@|7j z6@&ivUc?j{88a%wuAuLWe7fMSlt>`?#vO-2cRtKJMj??vs;);;GY*HSh&C8bv+e+w z_mOR2gl4EXkpOLtePE5;ri-O3);X{^rQ!%Y?B@sLs#NIKhbASmpv$S}ZH!#*w#io6 z>oT=l>iq>54ADXINh(-izM=PIf}W|A;|OGe{`GZx*>yZA1cg@`*p0X0Ka$+X(RV{D z5_m4VtoFkPsGwzz3f`@Ubl+=FRT>F(T}e&{Y=mKx(PM9TLaTmE`s#CixD%Kef#OT!d>`}MQGuAguFOD)E(xk+H zA?|%xQrsqHk?H4{qzNVB&Fg~~r6gxDR6 zva20yBVzT7=d6nSFTF&113=WiLlGwE2}hGzGQUqO+Ni@(+^hRpbDKZ0`b+NKY(!8= z2EY_I+*x<=N4)Wu+gnebjbxepOc^8e(llQqLJ#9gDDOAZ2J3(+O+J$2Y#1qK@Ckun zii+&@emt$3-+UWKlc#6+gL|3K`scC_ZoK>rlQPcXL(gZnKbM&^4n!S@OS+@Tl&I&cxVbKh0+x3hguFS>Nn;9bcFa=wX0 z?0$jF419g|e3jQ0fFDg0A1KgAU-%pNAHsp$Ssrn3=$~QJbY^5KTx)aBC||D9NiH9a zzt+*CWY2G#sfZ^p)gZn3 z%t)vCa}mSB@rV!`8=Z8PZPNBvN8|$kd|TDQI4&jxjK9teAd$ulE)jQ%G8qBY@6(o& z`Hd0&cG?;Cz*kDU%*rnn3wU&=d516R&f7ArRP{TD{u$nzJ1H<_JNLv`15>m3%g;-o zFu*~jh;Xweq4u;PNy@yDGwcSp{F%%n#^qN%;0_V^Ro7p*R?_o73_?;I*3f9&;c}mQ zr+#0ohB<#@%{MJMs;XLcXHao1TX$boT5YzDxAy{zbp&_6TV5hH1NagmS|mSHHT$`{pG?v7xv7Q(50d=E?Kgi*bV! zLM!9QxGPh%kIH)oy_R;w!DOC=bN66SO@=wu5=z|o-#`<#X~%j@TtgK`A{_3}LB1}mZU z2HjBA1ou&(+zr+=R#s?PKFF2_^*YdDD_TeL@%{O;4*@+=&ispJR_K-OhLU~rQJo8>RIv++Vz%W#6Z>NtXx!TVunL7f-hRBn$a{w zjTA?P(t&l>z@cu;qqcClXS}4HZAS(3@hn>}l2`49RSY)zc?Y-tr2>b1JkVB9MZp(x zbNPSr&~mTHAvV~|ej({J+iHkU7phl7TIVw8x67>N)aVL23q94Fe|Sfczsyy#9*}lA zU#c1Yfk}euwRsVP36tJ@Ag+}&ev5bhwnMvSxldPv&0^h9VLRSXVP~SD-h6QixyIRo zYeRMR-Uqizi+!P3LP4oixZKTNU1My2@;A>4<6gZbR>k5z0oalDnUhXI_HK;tYyd!K z1cRgNT_HQb8J)u1aYqI^jpjQ&m(!C{q^Ry061jlJNK{i2n5{ev*;=_A7ZNgbZngXy zNurE^E_?~o2T>g2r>?n^&{ci<-3nY1@jN=}X0jAeX5cL(YSBX)HJ_OoB76N&_9X ztmF|YPqyL0&pj(G8tj~(91ziikny<&e9zjct+Ks~ly(YJ4Bx5G0tX=!&wg&mQ#0U4 zt+Gb-Z_{Iog(IPFex=gU(FxEwR1$ht*R8juyd(AoJjXv6FB)rk4EJx0R3a&~Y*G;- zhcvQOUfp*!9o@On7YBDHgI{AhI|1|-Bg>pMHkX=d?aMY5_S7ocg>c1m6x)`PlB7$-c5xGrA9=MPJx;*l6*fc|DG4)QaRU zK8wUFq_T*-MIoROdcH~!f@SRn4FbMcl(}`Jp1Uo0d$7iZoXUg`8q%t)j3ZOZjxxPm zr&h8l5O9fVO#2WTeKf+|4yGh*=YJ3~HlgAxM*4v|5f0ER5#-n}+5gHg&@{s((oIn= znpyAFsgA9+rs-EHxi;roVc4kFszxV;%{h(4BabaG70B&T&2H!3TyKADxGoThl8xIB zj^?+j7y+lVEch_58hmZ_%=r4W#Y_zACLeN`RPbfGi52MDV!hqo$u5ap)i=AT-utBm zS0!L>MmztIh{RH@HkHWvwwLXz2tZ?IY<=e-RNteDMoBiG|V*9OcJuR!FVwJlEQlhThI@^Z9G)X(pH#Oxe@7RPW7LlGz(S z84F2uFO>`Ul2c7CW@(&u$@7i}llc+mmXQG6^#K?EU)07Qls$+i zpTHuaYBTdaF6Ibu4rbJ9e6DSIzysuk08R%VmQ~6Y$|jlrt}a#2 z{EoQ-!O{Byg^+jPtn>AhQ`d4lo3Vh~v=@%J@9ttN43zp;CNYr!x=1M}o4l4X+D#318}l05+L1}i?awc^ivL=C zxmB}(&a`83n{_zUERfMs_^tN2wu<3uSZFLoFJd${q9cuQKJbHqAWr85GqXbINybck z$l^>K4VJXZCGU&z9E-W_aO_*RPTtUC4Kae(?e*-6hq)w~j2`q=w zo_q3c#&L5Spi<~FJ0DM5T@8ji=x^8BrpN*TJ|*%iA>W+c?$y!!cmmVRMTuImY_Vb% zNXpjguY2*-yEngXl?3k#7JHLHE<-e2czB&tEyyT9gmIkzZtPd?RFlga(%^K^|UEIUj|uq0xE-0kVg zGo>_gbVxeW4q;rljk5BGU(AsRx@;!ExJNw!uI8$c+?{Uu)}Q`b*X6EEH98p`EYubQ zGz01?0fN-eNiaG+BcIdXHs;s>xJ0-q7>8tBcFV!Fv3BvX)_sI~mu|1LM*Fy@@i-Cc3#+dm&oTUSwS26~7+B4)d zq$Z3_-Mk)KK!#^og(elHibs^>ra#mtk>~JdN~!ui9q71EI}+>QJ<*03`Dv^YKU6<8 zdsQFT8Ts1ynroHlHl~lfc$CUNpIh;q9AEC_LOl%K?`1Ojp3I7!r^KaHZM*hM&Tc;h zXqbcJ-D|uQlIK%P)F{;!XDS)omqFv9UDQabl(lVFjyiI@9OZOsDUPv)jvD0h%7+?d zG!-kdGS`F)-K7$Ef{#p(dg^e(E&84-?!iLIwzH-8=6s%~3>egkSBtbKOO3aCzXA3l zf2!5fNy)jY6kX8J*&JVKLf89lBVy-|4>_!iO314YP-xg^ScCM8;1O9lPTQGtlg~&| zDZr7Zb=r10*5tAz@izb+bS0IWIZY_77wkB2`ZF2&HGIF+S|d=K#pc`&##ECVOi*g z8RE$OmA)q-g`Sd}hEdDIq}Li0dUY>uyL?fmJ$owWSn<_bI0}9)jMuAKuq*GaUJ)^| z%FwIv{`F~XXU{5p*wX4{`gdA7zw;U4)N~rbMj{7cz2Quw^wC@ehL17Uz~4@rKTViI z1$Lbr|C=;(^?^^;E${gbf@#sshmDKBffr*NEjHpQr<$bXY&_k#ZDzcw8|h-%pFfE; zgqr-?wjSNo0QXyASR6`qN>dKTLOq^PL&0X!0tbHF2=r5X=vJNC-)6vnP-tLLh-kAr zZt?=9vkpP)>NqBI^XVdndyg4U(ETj;z>r2+f#|ht&3qxOT%@5xfquNXT+@0iGyXE$ zME)ruRV4LDoGd2WPOvNN03~)7{cM@mrMV)nZAa*^N&jCtWgroaIB0V1IhJ0y(%)y7 zSL-+RRIppu`~4-Awc+XUj`imT`x0$K-mI#WI-a^(^&bYDg*f)BO*&X#GblUx0-5Eg zMw7V4zV10$Wz$h-%^2CvmZweCF|hqpCu#VhNwg&Wxg{lA!#rbER0fi*OKv@&#-XwXnA+}bj>|KL?;quX%pOPYL@k}I`oXM_i1;tp4TLLSTmR^8fCIMI-^vDSo$lFVu2o_fkb1xUA}eJ z4=&WJsA!(J_qI4rvV}G94MWYbg)GvRPN|5#(r&RYX%pNg#0Ag!vYE}<+sssi)$x^P za2(XslIwr5M5i1`(YSn#Kgdx^#LZEPN6;aYBKA(rMLCwAc z4hM@A0}pi90(|klo4+o*AvVbBPgnyFB3yI81795s}9j%X}Gl|!*E_G0Ep(DG;g zH}UGJfbKQshl$y(WGpL6r=HsariK{bWR)i@TmRhIBiL1--(yH| zdgYea9Mj&`KS@rotL%^;6WC)Y(X3B5H}AFV0Vf!i`??{YiJ|a_1MKFyc2{0@F{I-{ zjmFJMt&_&AeLmL!%LX3}qxH6WwGfvgN0 zldkoAy2*%O0Te>ueLTo!wIF)nwzvwq^DmY=3Lq@t@0EO4_HGA8n{D1DSI>9P2Xxn7 zEK_+DorqU~H?VRmKtY}9*3kCwkWpi@^~*eu9$a<%bY^l8xS6)9UN(Ty2>n-6A_a*G z3-`Ai7Q+PZ#I0~Wto_WYq0k6L)S(s($oHvCarIn1mUE5Sg2CgmBi|<1hzCsp3_N21 zWnSRRqax2~0sBt=xL`_4D`!g2t7p-1z@eaOdz`DGp1ipn%7i|VHec&b$BF)`VhR(~ z<4__G`*wJSQ$l{UfCSa^L+kw1poz*8oFOQ^9V}dKMU+5g<{tCo%x5>~5jzp_6GfbnB z&0|$gu;pV|_@-FBMC79p{r`#sN|nCn3|t>)A#~A63bFaSC|Er7YU}Ce?K)) zKrxsMCowa-XQfRVGOeA&oR9*?!?(7(MSSqsqu5T-QDw545|0R-Mroo!wmCVo@trZ{ zF-Vj60A%Szyp@BWjLot;vuwDPixuDOoc%TMK(86#tP^jA?zC6^k9KCn9jpJ2G-RQx zww2Y$X*EkGCLA_OJS&bCj$cd3CZ%h%%}MLtImk2^cb%hPR`%7h%2A=KdwHog=Adb)C!n2p=<)9+~=Y8;NZbZ+{|(jF$g z(%3;YGv)dUio7q{xaW=27JFG9AS$UbX8A9}gcSP0R1%ahOKzlw@=HYpGpXH!R^>4! z8wJTN+9i$o9r)z=QW*`}hsumPtUyR}+Nz1YCd>Cu)a55R$U~ zbk)`9P*dk~Oss)|6|}7Cvj#G@dveBD)7#6U0K<1GO#elg`id4H;t``U0g>aBKiKP_|ACakX{@1v*GJ_!W<%N}I?cq_rz0$$i#xNn z&J=ZYX38w*4O}KrI#UjB6m~(bUDl&?{2f#PlIAQI%V#}CYg&oi??=V~CNpnoI&YW! z3o(M?l>;kIlgC<8zNi-ZK8GfsIQy36fi?-dQRUiXpeymc?o2NtA$95t$wK$A zv8o3;+#HOu^!!_z<%8nYLwkt@e;-XSi+5)9HMhR!pc!x+$sR@Ao&~N@HNISO<@B!y zuuP8&JeD8NH&*@$?RHH|G<5bUxQ)8)_V*+Fj) z@D(vyXZCr&-la(0YeWr*@h>&1Z>tQ_;pMp5lc=dkMv{9&!8v zQ_(uDzxPx>Z z<`jOk-{`KreNU4o+QIW7F2%9Hu9O+o2<|aK75Y2kU0<#v08rZO4#+5*Pnir_ zW;{2$*qVrYlbyEko!UsPGF`%va9D(IR(+kkx8tO?fjNx$R_^H8_q17RHQ@2YbeQ}) zMNvpKb#2ld@wUAzGsI^2dL=W(xRv%utJDeLNnmYJ(9qIj z(jr|npfSTmX&Pd(8S+>Em_(DrBbo04*v>rC*l1+s=;%PanH4X>wKPRW|GqDnler4} z%cKzmWD$fOsYnDLl;5HfF)+8Ci-wjLu`3ko!hXXe&AM7QPWL})svS4R(P?rvpg@;>nBMT8hv;vCeu@ zGyqWk5F=mT18nve@q1vA0D7hc=mj9`x!;o=_;Np0Z6?*c`Ne)S<>&3HZ{;5vQIG*@ zOEL;?5D-e8Zt-k{Vmuk+IzVwaD)Jss1QMO32>L(V`9^=(ggcn{76MJ}h{t751Be!C z`pvsT;X%_1-1mavo!*bcV50J^F1xV$v(IO62U$c}ft;kYD2#8i$;XqdK4HnEFd00_ zCa){Chv%LGd&H~e&FRXLT^7Zq{heeUa?dT+CMEim1$840bdfZm22u`X5g|u+z#hfE zZUQ!A&}s9YEHE=HrV3=@>|u^p0em^Cmms!DhjG zFKY)p%$#C5nm$-o*>QMAKO|(WNdC}HKU9}rSh%g^GP7cRU+sDj`BCwAfm~-$i=1Js zn;sT#r&1h<#=Bt4YctP*h{HOTdw!-Vf!yGVX{m@H$XG%>o^0pyd$Ul9Zzp-_NPXJ! za=fGHxoisO`_Mj*_Buec&xb9 z6N&HxL|f-s0SX@#J5d~YZUHv>UWEEl=;=IUK79d*3_t(O1JsD%jR@k0H+x;M#K(S{ zS6~Ww6WoDbJQlR^I4PooVF~@-9ugu_!M9S@%`DQEuYhdmX8y}oOu$y%>U$|74h71I zG#Dln#vov@Q~^~Db?56#t8fD_xv)WFJbPZMC~T*IbnV@z2p}Ro1cct#1JiGx?1g{` z2rjo{<`WRuF_c4Ogn(8<;%HVUQN-a8dl4xDh$FrLh_6&#fRJPJvd|>QJ;1E#<@t6E zF@OnY9kfaDQ68OtV&_W#~;=Vl~kEv$0OILife@v!$%I z7(Ic>J$NU~srp@5fn3&JMhuI{ryJyLK6gyE?HM(tC~=H(--6NlKN_ANmxkv*LMe%` zW?VR&Cf-9Ku3ir_waNpMr}xoC$}#sHB}Lx?ZV@;|BA3ymNg)r3SqLAHA67I@V&7xc zYUlILqcckznnl)BA1hZ2e(8NT|KP zvXoArz^2$Peet4wBOKok3s4AI2*DYj_kWBDBB%w!u~#8kdaeoZ^l$u;?WuoP7QtfB zUbnh(pTk�>Wr{#PWGEqHWvM?CjIU*k`B|BTx62g(4wu4I@9I@IWC=b-mdJ_D?E6 zxaAl92C8giGRR6fm%aXu$M|Cx32p+CrOTx8{#s|}>$h(o1EUa`+gJ=e5T5a%dVTfj z0%$Kx=e|y%WP0o)w4Z;2_OkpFH!(LizoY+6QTm-Yj3A3;bPtF@%qmxoc*AZ3&AS3qEQ)o(q@;v17IqMR>@ ztVHq69ZdlzH{YcP0+&=N^+4rUi~G@o>&$}NdxuN+WOf7a_j|gbr*ncFpTiRA_|(Py z+xMf417gww9GZ98HtmtivHd9NjMjQ0u_UQ(qJrR#-gh1C0EKj`tLFzbiS#%g_(C2l zj4F7STREMHYHJtQsO^rri5`Wshzt(8rxo|o-ZbgrT%~>b{e9+#!IJ;0}plMDR6bt4g>* zSrC(>i3F4wy5SdFZf-Ps(GGO+*Uc@e7H%|Bz{{BVd6E)F6u*Zo1?aH2vNJkJXp<66> zRET+m8|k>8lg4Ejzj6+%j4J$`zyP^C+oY<9+WFOVJ~Ib8D&h|j;kVD7RYnB#K80$X zq${7~{9hV4p-o94HuB6?rX-uJ(`c~^B5hkaY_*M;B^qeCnbXhbr0)--=eJ`j`=#E| z4vf_?2i{yL_}nfvO(Jz#eL`(?TYQpklIR@0*^?AR>#VJJT%g0fJZ3SG$lHt!i%h|i zs*%X13Vm0`W)mha@wV8##c} za0!bTaz?7Im}DAFnoXVy$Ia9b>RXpRAC<`+BEnpm`d+#lOU};`X$6||M$z_=ZR7A9 z;%&-pvpmSIcjaf+POK1;A>o&)UdoZpuXQjEwV*hO0C&2`b12@lTMGeSRHyR-h&?g5ax}B z7XWvL@1k7%eb~A$S0+5ongPB8`&cT$OZ$(Sc=ZY;hp7O*1UN`NcOOorBIo}ExrHA{ zl@|exA)MWPaEC##iK<*nQ_R(OsnvvQ`uTwvWYNJOTe6B(4#!iO7Q5_2Q&XtWS zdR=pj@UO8jTd%PtIj`@V`F9ekUzU44YY06gm_j`?$Bd{tk)I7~=B$`Waa;8FBM`AI z5||2gh9yL1G}25pq>-)usNHCIp*P+AaG`Ul6mfXhe?L*P!+p|1u=G^n>u31xwAK+| z`QF_lueus*9+9e z7K6W%PB-LQlOs1P72Ru-5P$6-3~&|hN<;?akY{jP%H#|`4!cUNkY&u3r+k?$8<1?w z;`S)zSZGgrVl@p`2km(rw6>~$Po$;aWk_}pthm|RjZPzT_p~A7yB&G?AXYH5R-blU zr{$}2b;OW!;JsCJKc-&m*eZ$ejgQgSGu~TBVOp$FNaF+dMuijEUb12ivunR%+*^kX zxmrg@k7>qReb1C%Q+r=tRI7D&qytLlN=O40Q%R#d>}cKfxT2SH4zn&Ws2^$M%9vBM zy^POIlpPhnYq#=y{ZxYeoS$QyzNDZy>Ek6N$LK8#I;artk|Fu!`P`Okai znJ&CsvKA+ZO9?T5yU2CzKMo-4&b5BF&=73^8pMW3u_ml55Qfa3NBK6k*_0TKBy?z!=T4=GZaY_guoki6vGhl@ zer%Flb`9|Sw8I%N}L2X zU9$g_WvA+kf!G!Ei*zoBug(pAi;*s$It{4KKC;oI3VesYy(ZAJyw>Or(U9l16SaFcs!D=^65Y>RNE{{p9QO+Uxz%Mu$|>sSEKls`Pag^jfi(wsVdxRXp=5 zA15(#zfFHw48(PmkJJ+JGHTJ+9Abj?>D|p(qP>wjnqY17&7$w2K2rG3fU09RjUxK_ zVl?SHF0q9 z=NBq26Sbgw{k-x0LEm_xY}Vma?usJYLp5r_k>hB$JwOjCp)=0%Y_#2NQM{xACY^ha0hIjBjzbF#RY1wt8CW^#Dyxw zSW{;YYJM+*%{$)B_HNucIWCocv>LWr35%onLn*KR8j4%zWyABL>MT`-(`99kvGj+O@9fY%ewLUJ?}kNrGI^Vl_M}m!De-oUw``aMa}D_GSZgVo}rVOCPXvITBY;R?m znrRx|OdiCn6PXA#12fRvL3E4w|K3w)kuUd8ek+A9GbmJ-PeRTTh7`|KAkvw_5lq3d zXLFmj9P(W=!Q_*jwDbc8oMrr#6sr?CLy>Fhw?2#x|MI8sUy1Raei+Ruf(Uw>TD4}I zs$}6Wr3e=0rZn1;;33umITj~?037(12BV{7B&6vdolJBu*@Wts=g-dJbkKHUXcs|T z<~C*}a`{VEGNq!NqQcYW)8Q8urlOp}`gb!b< zA|Z%LXlHr&_CP2*g*N9!o2Ec+f>=g}g%M#Z^AyGX{zHBgdNyStf!EfaZjJ4a@7rwb z_!I`(gyENTpEzu5Qv?ZqE>(Tl3cVhBO(Y&HA9vt}WQ2@p*;>t!zuKKVnSB1i-!hqW$;CkRh9CRmU#9pEpoleu}s zFwVV;cSdC$rF+7W+hqtZaxWwTQlYy@KTsgkzss`;dHt5**fNN58Y;$znD9Y**V^Ah znz+tzLeM<^9C#<=b}TDRJvgx_P?m>OYd^tNKKqUXFVfE_y#q1ftGsfaLeFh#Q|h*Qpm{F_b335Fn72bRIaDA>EToXpVP%}M%EJ?w zl>e$K*30)-MLX_Pcs{OvB}5(9Aw@plVZTX(G6ec zp*dne(40I(XaK4$Ps7@nx7f(#DG6mOblLDi;j>M(#&MH>r!9(ogKrAqYjZ=&Xnc^s zs`itW;IPKeSOs}6M4mhd9yZ=Oq#u@Ov&g73z&l_>33Q`c^|&bjOr zS0u9_^6^c6JX1MR4`}L7z$z98vRX4r=GYO@0uU(`!oPW{n79qW5r&F^=dKf&T+Xe0 ziwjBnhV>&BJxWdjslg{^`x|K^T~0n{Jmkp`W(=x(5`)wxGcpVXEGoEzwM}Z63TJ6_ zY0QU3Yb{qHaWv5vk~nz8G*{q#247cV`Ly6}bL8k!8SJR{57+W9CW;ZnfCsza4QO=j;QSP;48KQ^%L{|A@XZckP`Y_OBqb8&LCY0I8W$N$VUO{U*LS_Lr zh!SyPzU-|->D_5-DTTYLCtZDP+|3DO7J>FigP&;$Hs}1AO9pAcK0TM z=b@EQ4ZeoQBGi;cI=ezT)#yrqG8HO5xo@U_)m7jf+k*S+k0z_zBIz!cW3vu(RWCSW z)6fDCX1@APau;C%rl5=FeXcr19hQ$;RjMzHmX z=-|&aCaptXA>UP0Z}8)|Ab;D~2*86Q(Ai`T#l@J>sE<>}K<~CQ!(bHnG41uuq{lu; zW^j&b=TCPaQJGKydUmBgy{~1;fx`Ug5?NQfMAW9PsZTe}$`mFZQ^5OVbBi^$L zRYyF!ym6w}#N$4Go=wz5#x|9hK`>%y5URvrUlPxlWL=mkIuoxQqc@BS8aHt(dX8Q)=2L#IV?!f&yR^Y7!kOb&vih95 z;q_bBM!KwX5*i+Vo!yLM696Q5{F-~^V(z-5Qi@FeuNRwS=^&eMX=K;Jucldo;1P6} zo+hA8oMS>jR7(?*CZbHZU6ly_f}Gf!h7UPtAFPvvWobmdU4%czh+OuscZAO~?ilB( zKXz<*aS+&uPUKqr=nh~|^Xzs;6@(3WiQ-_^mE>sApkX)R{F;7obnVuhU~hCh z7VA!9!$cJqsj1n+c@Od7zHGOW)!bxiLk^S%Uu1p;kk{t)f{Uc0{aUl>aekbd*7KE@ zd8ciAI%tsl_Hl~~)+52-=7a%XlSfM7+JN^>v;9@b%lD-NSU91icIM>^)q`fYCC8OE z4XXBwEQWNJ`O052O>&3_Zc~QYnNw{)GHcaKxl6W2nmmxcUhC#9)dMegBvi|Cy7I2b zUcJPlG3kc%M(EdnFrMQ1WtkL1zesBP5NmyZr)&g-0bW874}wFKC_0f1fn(lV%smk^ zVdCRAm+owC!|W~pCmZ_-eRzak3_Ly&YRj&-kn&v&G+kR>2QEr1Ka2?){zynmL?AGY zsontZi_wPs7^Kou!*J~~6RE(Pb>>*vpV_?2(CJo}GoVZ5nZ15q7P=n0I$%bmWyqFk=}1|s^SHUYgN9*7}PaY z-!+)rHf8ZMqD02In?~Qsv=2Q%Yr@<@mGXlt@sS~44#pcl{OE^Md80tzvs$fh7%bMh z>~Wu{5Yur!R7H?r%?Yk)_UrevZm)9{2AJ#lMdI0PvK%oS8Z(&v#q3)Qv7R zk0b2kUhMwzsrzDE`Dmt8Ivc(N{sm!y?=`Ut{7kyai36FgRA|}#HAk32ET#RJL!eO< z1Jka|XO$nm-zE1bktTe!Mz~O*Mj4V8)K(_Tu6+)8e(&vy}>W2<5DV4h<5t-+60Phk)JhAYn)BYu0zBoJex|I<7lm zQ>dXKIuS_HxVAhQsGcz-NRRS%pKEZVNpwCk(P47xtaS)Bv*B~9onSDrD;_rQGei9n zpfeWA2*QdApY;kk%ze0615l6us&chDr#zlKSYmY$KMJju%zCspoii7`Ag*vLW@54j zH(j>vibGyz(RPKglz+|?fu>u79&pE;(!@{^&{}MmSuuV_W>QyP9KvHj zU1M7`Tt&1T$G|k8;>HowcM6^le%$a3o87I8d5>iE_?cXILJ|)3ag$Fk+gk^edG+X3W-QP( zb^+(nS1SOL0l@=sPy6r=EJ=-9n-bwI@c=~z^KCYs&>&wgZgmmpB|M#&d>7PH&Au}t zklV%!%uGQ?k*;D|2rCN(cmvIrB!ejK`j~`tq&ZFn$l)ZaWx5})Wjjmkws!7%zpaTCpt%`9Y@`AYtDP(|yb9Xa5C=&$6TM*! z&?G@XT{-c-8OZcx30#J01SxZ@&_F~WpyNa!FoujtV0Q=F8+<2EUe(c3vInQp*st5{EsmRtmSO%037C^;-568F!o;|!$>hy zMTqJlK1uM#ZHDl543Q6SZ~c4B5u21O4^*KVfpvvMF=u z8(GUye_kB^8pz3gI4RR_5i1Ril`rTQKqaB#6dLZtwxf308>YZtw+)vehfUI8=bQUT zIk0!GL2el;|Ksn;(-?q!Qz0l@3eXtYIYaRdQ&&{ZrSvfSQ6Uv6ID5PK$4J2k1{rb> zz~sGOPz^m2i3U&-)#~BU%oxPM?Acso9CdvnJ|95UrA7jc2SG9+ngIkS+L=bF6ldRi z&jej_?Vfxzu0dRK@~@#vs}SS0#~u-7uqH=UZ^>yERhs7Q@|K)CusMc!dc{rHU;B7= zTpPSu*cHP{oiTo5H2o!d?)m=m`u;>3p1BVTHs>B(832WMQ{7@Tp7a}N^aPjk(^ru796Le2W{2Ul2d?PqEqloR_z49uqjK08Jo=H zT|}^hNuYMU!C_HatwuxTCFR##ENG7mEDZuTd(!q6F6rjXC9TStYMSIP8mXBH#M{_F z${rTUqnK>s%5Uxel^G_x9f?4320~61)PB9Ta9{Ewcx-ZjfT#lvwr|M-edrHWDTK%=HF%mcB?$-Kjg(i~C$GJZmmXwQV443H* zw9glY$nw(c_CbQlogq_9eh3NN?Dm{hRPrLi*QwmSi5IktF;czS+}ynZSmA~QAbu&{ zMcd1U3^R_-j=aA(A!YkQQ{wN>+Xv$v=@&}4bL*Ea?Yo5z(_XCp*7cmjW!$5qMy=^Q z;7NIWj$-V&VP%k16)4MRnTUq9tfK+c{OM50RdL63u5z{)8_Cq0&58=ZF$fIe>&`{w zxNUJZ)@%d&12DHZ>+UeA``e8i4-Y8kv6Rd==m*<&2b1{8nZ)7SC)>*~1I-no#0yzp zRC>4)0Da6Q){S7;w?odUzw&XKdftTvZ<|cCld&b11^+dW&C$)T)4Gd@`ASzIi#Zc{ zO;WT6+_*TtH49bEr4y+YA&1_U#OY}I z{u<_5@Y#z*9+PaIfw|H-6Da0}rU(b?-9;gR4g%@ySl+?*f7&C9b@>^dzsJOTt9X8*J9LBzn)6gKEki9*z#-R3Ddv+@im;22YpmO#8}bm5W$6Z zV{nZ?^e=8UroBJLA~W>8v%zb{NX?x3;MczS6jbTOS1(ogNbddVe};&wq|YL44DHFG zm1eTto8_G|+iUf{pnAs3-tWqcYTM7J5FJ4^`RgYLgTr-OBb(vC=$( zWs8+I$o3@RO{3(AjB-G{7)?&4ljwTAR_i`?k6)c2M&S<2^!2p{efl~=2y#PF8MiuQ?v zHY|qZipNGM3!XWiTK-~z2KMSSq;0;MFRtBe zhMFg?98Jq&_iCf*8whq|NFQPL(he}=JS*nUk~^bEt?K;}2KmaH1J4ghO-9VQ0_rzL z9X9~IbA2U7)7IP-y&W4QLCk01QerH~!IS!V6{axpn2SzL$xIHjeFUrI@Q40+#rU>VxbdV(6N#VvCd1L-rsO*Fz?9+aLWD41#kMfuR3s-J8Bk$_H6o zpDF_-1bJJ5Dua)2G%d`B0Q(3rMUM|rFg&>v+84%+O2!CZfO8|@Y@jCFp5#LqWHFu2 zp;yznOBnkj@5<6=vu9J+uT`-YY{hNSsKN^3wxf$5K;Pb3<#BGU`0A~eK0OB?ZoYVwm@bS z2otI`7S5m`CeWy$v4|CP6z~TFobD*kpqdFm34c9`p}S)=iTXtgt2>={~Y0%Mm1s;ccWsuUYS*s;ZzdiiK zabHg7>JPpqWwgPEDY~U{5ai?=!Gt-6NfaI+bJ9Zwi^+WX#n95%Ko$k!aH!_{acXw@ z&*XaavgQR&_^@pPCYJsksFkbWhcfpIz)b6?*Oa~UjDbK; zVY=ue?AGe-s4$MutCrY}k8|Hw0INuDlvy=OL;38oK~`O1db~fyPdlpA$Xe|4Slys6 z#Kl$TYr98*@mjn=hu3X?J1_1WBHH2oeGoRkMnAEUXXEs0^x~d=nVM|d;6-aE+vNDA zi1R&czj~d4Mv5b0Y*iJTd4r^JlDw4Z-7wG!UWcHJ43zeIfor@nipKetzzv+h7pF{uWoC z-{{3F?X`?$pAyud8ky{)g-F}ie$+9XeWPWqpBvF{|8gnBmvFgM+|i=@p$2|u^igEn zu%(~4FMxz1FZ}uWbBBP>^99FqJ{X_#cJRWEagZb!)|>2SgeDdmW&1UE9G<65E>ogRHj9gJC||13O5Eni@{*k6gq^vN|XGj>4m-K zuXvn%5UhxAO(YT{@C|1TJH!*QZEf#po%5^ZoVI<>wm|=W_y@J&Q1y)By4uitP`SHJ zrfvO;OAxN42=4Jb0E*z|F{%R!q`YUh3=V)Y@#ceXx@&oM{iz%2$@KPi07=`XX(n{Tpl!- zNs=x2m)KoL?eWgmZ-Q~(MZ8>hXd+=C;bUPx)IO*G75+#fBF*?3EXbhqm%X}nE-jxp z(OscQd8?W1WqQ~69$fYeKLXz3b8JSs{a&lVK1J`+zDQJ>HUG@xh1Kfz{sHO(c>aPf z^+iOw*Vkxr1rAS>i>*zggll!GOjWdY4slSYb^Q4B)Pz>7v4^KDM0?>U#p}SUorkGx zN!Qk<#U>8|;4#*&-C2>`0$T)+qFs>^le@mw&d}iy`q%YH`LuOe2BnVg_X(63d{r8` z(vj!9$y{yYr{errA_Lvy##eZEK_bI|t*>lH*2aup=IbSy#>4sr%byNwN8>Rsu-sHM zy>4NaO4Y=UYP|6+H3AyD?60z_jPJBXYgNh-r@5eO!jHiOdM%T*AlG|ZDf-NoSq@as zZu}075V{l^wdXI;g7S)O#|io^HIXVhCU;|h^?F5&Yi~MLPN17uSnO~8Iuzj<9t32+ zv$WEqQL2v54-qM?a|W%fBaEW^xCpGqugQ4UII$eGMzNd71K}mo870i1v`j_4?lG=qZtGhVYHlO)X4)4J5>jK;x0mJ<8ikt4 zBbqPtQuDYf7vs%O+?7bNb{tBgTR(SXx%C=puSr6eYZ|DX$b35cRU+WRp{wbMAcJ!8 z-3Ld4?5ARme|sg+;ZHpXOMym%OlFCS%Z9Yiox$SFL_Egi^EbeWjaZj0NZH-4?o#Yd zQt5iv7aCJsO0`@FAg`ZS6=cwK5*X-;LuN`MxXSb{6a_)g5x$%%%Sx}X`$g0T29unN z=vr_JF_7(dwMwm341w{~C7x!#d6@GPvZXI8CCjI$*<7Z4U(RuwNARtN3CEJt{$yQE0}ux4TD~;oV#@;hwtD{icws}nZ@;@t*uC^?W%-sS1?i> zCPe=s9?NCKG!y;QgPi|?bMAe%07kOlIb=bKOs*&?_kDC>H_1`fP*{qw*90b&KmC)v zvn92Zz>g?h>!WBGa>aD(d&lVRWPgUtxpr9tG>-cg3g@_q5ArH`wps#$+wYj}RMkn0 zM~6}%3x7>Nic&kg&TW&P#rO~V=56CW(38HE8a}3gAvTVG)Soy~5HCooW65*ePJ3jc z)=|80Jh635NUitvjGYCCqJsHx7e*;@)?!J=^K5=PPt?{{VvUiNOeapGjuTXV7J{V-0j=&WB_OgI@IeVnV8 z2i@JZsY6Le5~-v{6&&LhvJFdIi@-#38Fh941FR_FzPiC-$uq#&Vs|p;4i*0%Ger0t zeReL?+@P|6J;>8qONa;syh62>K8(QCIRB+YV4qaFL%6at;()c52_V2k#Rp^;cCW&+ zzLoKYq2|El+yQ&-7a&uBK@xYM3)hP?4^q4v6QH2N4@!dd)m8pW z$%{R26=ACYV>@pwhL>^TGZwlqoz$W>wxu_F6R!pc%HQq({bEE=G;Mxs@&F`Dy0a3A z*TSGtxcjN}6$=b_$g?0ekemc_VIw+1A%Nn)keIV7=y_10%Al2&Ag2S4%jLtE7=w^Q zvKCVA-2KW~fp(ez)Q5M_2plOYxFB1|ra9>O9iLtIS;HXrz~o?gLyW9>vPGS&lae(g z(GGp^_W$^tUL(⋘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 literal 0 HcmV?d00001 diff --git a/workflows/multiapp/components/statestore.yaml b/workflows/multiapp/components/statestore.yaml new file mode 100644 index 000000000..02561e72f --- /dev/null +++ b/workflows/multiapp/components/statestore.yaml @@ -0,0 +1,16 @@ +apiVersion: dapr.io/v1alpha1 +kind: Component +metadata: + name: statestore +spec: + type: state.redis + version: v1 + metadata: + - name: redisHost + value: localhost:6379 + - name: redisPassword + value: "" + - name: enableTLS + value: false + - name: actorStateStore + value: "true" diff --git a/workflows/multiapp/go/order-orchestrator/go.mod b/workflows/multiapp/go/order-orchestrator/go.mod new file mode 100644 index 000000000..bab765c75 --- /dev/null +++ b/workflows/multiapp/go/order-orchestrator/go.mod @@ -0,0 +1,30 @@ +module order-orchestrator + +go 1.24.6 + +toolchain go1.24.7 + +require ( + github.com/dapr/durabletask-go v0.10.0 + github.com/dapr/go-sdk v1.13.0 +) + +require ( + github.com/cenkalti/backoff/v4 v4.3.0 // indirect + github.com/dapr/dapr v1.16.0 // indirect + github.com/dapr/kit v0.16.1 // indirect + github.com/go-logr/logr v1.4.2 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/google/uuid v1.6.0 // indirect + go.opentelemetry.io/auto/sdk v1.1.0 // indirect + go.opentelemetry.io/otel v1.36.0 // indirect + go.opentelemetry.io/otel/metric v1.36.0 // indirect + go.opentelemetry.io/otel/trace v1.36.0 // indirect + golang.org/x/net v0.41.0 // indirect + golang.org/x/sys v0.33.0 // indirect + golang.org/x/text v0.26.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822 // indirect + google.golang.org/grpc v1.73.0 // indirect + google.golang.org/protobuf v1.36.6 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/workflows/multiapp/go/order-orchestrator/go.sum b/workflows/multiapp/go/order-orchestrator/go.sum new file mode 100644 index 000000000..6fd6362ab --- /dev/null +++ b/workflows/multiapp/go/order-orchestrator/go.sum @@ -0,0 +1,62 @@ +github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= +github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= +github.com/dapr/dapr v1.16.0 h1:la2WLZM8Myr2Pq3cyrFjHKWDSPYLzGZCs3p502TwBjI= +github.com/dapr/dapr v1.16.0/go.mod h1:ln/mxvNOeqklaDmic4ppsxmnjl2D/oZGKaJy24IwaEY= +github.com/dapr/durabletask-go v0.10.0 h1:vfIivPl4JYd55xZTslDwhA6p6F8ipcNxBtMaupxArr8= +github.com/dapr/durabletask-go v0.10.0/go.mod h1:0Ts4rXp74JyG19gDWPcwNo5V6NBZzhARzHF5XynmA7Q= +github.com/dapr/go-sdk v1.13.0 h1:Qw2BmUonClQ9yK/rrEEaFL1PyDgq616RrvYj0CT67Lk= +github.com/dapr/go-sdk v1.13.0/go.mod h1:RsffVNZitDApmQqoS68tNKGMXDZUjTviAbKZupJSzts= +github.com/dapr/kit v0.16.1 h1:MqLAhHVg8trPy2WJChMZFU7ToeondvxcNHYVvMDiVf4= +github.com/dapr/kit v0.16.1/go.mod h1:40ZWs5P6xfYf7O59XgwqZkIyDldTIXlhTQhGop8QoSM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= +github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= +github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= +go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= +go.opentelemetry.io/otel v1.36.0 h1:UumtzIklRBY6cI/lllNZlALOF5nNIzJVb16APdvgTXg= +go.opentelemetry.io/otel v1.36.0/go.mod h1:/TcFMXYjyRNh8khOAO9ybYkqaDBb/70aVwkNML4pP8E= +go.opentelemetry.io/otel/metric v1.36.0 h1:MoWPKVhQvJ+eeXWHFBOPoBOi20jh6Iq2CcCREuTYufE= +go.opentelemetry.io/otel/metric v1.36.0/go.mod h1:zC7Ks+yeyJt4xig9DEw9kuUFe5C3zLbVjV2PzT6qzbs= +go.opentelemetry.io/otel/sdk v1.35.0 h1:iPctf8iprVySXSKJffSS79eOjl9pvxV9ZqOWT0QejKY= +go.opentelemetry.io/otel/sdk v1.35.0/go.mod h1:+ga1bZliga3DxJ3CQGg3updiaAJoNECOgJREo9KHGQg= +go.opentelemetry.io/otel/sdk/metric v1.35.0 h1:1RriWBmCKgkeHEhM7a2uMjMUfP7MsOF5JpUCaEqEI9o= +go.opentelemetry.io/otel/sdk/metric v1.35.0/go.mod h1:is6XYCUMpcKi+ZsOvfluY5YstFnhW0BidkR+gL+qN+w= +go.opentelemetry.io/otel/trace v1.36.0 h1:ahxWNuqZjpdiFAyrIoQ4GIiAIhxAunQR6MUoKrsNd4w= +go.opentelemetry.io/otel/trace v1.36.0/go.mod h1:gQ+OnDZzrybY4k4seLzPAWNwVBBVlF2szhehOBB/tGA= +golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw= +golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA= +golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= +golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M= +golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822 h1:fc6jSaCT0vBduLYZHYrBBNY4dsWuvgyff9noRNDdBeE= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= +google.golang.org/grpc v1.73.0 h1:VIWSmpI2MegBtTuFt5/JWy2oXxtjJ/e89Z70ImfD2ok= +google.golang.org/grpc v1.73.0/go.mod h1:50sbHOUqWoCQGI8V2HQLJM0B+LMlIUjNSZmow7EVBQc= +google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= +google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/workflows/multiapp/go/order-orchestrator/main.go b/workflows/multiapp/go/order-orchestrator/main.go new file mode 100644 index 000000000..a3604dea3 --- /dev/null +++ b/workflows/multiapp/go/order-orchestrator/main.go @@ -0,0 +1,218 @@ +package main + +import ( + "context" + "fmt" + "log" + "net/http" + "time" + + "github.com/dapr/durabletask-go/workflow" + "github.com/dapr/go-sdk/client" +) + +func main() { + // Create a workflow registry + registry := workflow.NewRegistry() + + // Register the workflow + registry.AddWorkflow(OrderProcessingWorkflow) + + log.Println("OrderProcessingWorkflow registered") + + // Register local activities + registry.AddActivity(ValidateOrderActivity) + registry.AddActivity(CompleteOrderActivity) + + log.Println("ValidateOrderActivity registered") + log.Println("CompleteOrderActivity registered") + + // Create a workflow client using the new vanity client + wclient, err := client.NewWorkflowClient() + if err != nil { + log.Fatalf("Failed to create workflow client: %v", err) + } + log.Println("Workflow client initialized") + + // Start the workflow worker + ctx := context.Background() + if err := wclient.StartWorker(ctx, registry); err != nil { + log.Fatalf("Failed to start workflow worker: %v", err) + } + log.Println("Workflow worker started") + + // Start HTTP server for health checks and workflow triggering + http.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + w.Write([]byte("Order Orchestrator is running")) + }) + + http.HandleFunc("/start-workflow", func(w http.ResponseWriter, r *http.Request) { + log.Println("Starting workflow execution via HTTP endpoint...") + + // Create a sample order + order := OrderRequest{ + OrderID: "ORDER-001", + CustomerID: "CUST-001", + Items: []Item{ + {ProductID: "PROD-001", Name: "Laptop", Price: 999.99, Quantity: 1}, + {ProductID: "PROD-002", Name: "Mouse", Price: 29.99, Quantity: 2}, + }, + PaymentMethod: "credit_card", + } + // Calculate the total from items + order.CalculateTotal() + + log.Printf("Scheduling workflow with input: %+v", order) + instanceID := fmt.Sprintf("ORDER-%d", time.Now().Unix()) + _, err := wclient.ScheduleWorkflow(ctx, "OrderProcessingWorkflow", + workflow.WithInstanceID(instanceID), + workflow.WithInput(order)) + if err != nil { + log.Printf("Failed to start workflow: %v", err) + w.WriteHeader(http.StatusInternalServerError) + w.Write([]byte(fmt.Sprintf("Failed to start workflow: %v", err))) + return + } + + log.Printf("Workflow scheduled successfully with instance ID: %s", instanceID) + + log.Println("Waiting for workflow completion...") + waitCtx, waitCancel := context.WithTimeout(context.Background(), 60*time.Second) + result, err := wclient.WaitForWorkflowCompletion(waitCtx, instanceID) + waitCancel() + if err != nil { + log.Printf("Failed to wait for workflow completion: %v", err) + w.WriteHeader(http.StatusInternalServerError) + w.Write([]byte(fmt.Sprintf("Failed to wait for workflow completion: %v", err))) + } else { + log.Printf("Workflow completed successfully with result: %+v", result) + w.WriteHeader(http.StatusOK) + w.Write([]byte(fmt.Sprintf("Workflow completed successfully: %+v", result))) + } + }) + + log.Println("Starting HTTP server on port 50001...") + log.Fatal(http.ListenAndServe(":50001", nil)) +} + +// OrderProcessingWorkflow orchestrates the entire order processing workflow +func OrderProcessingWorkflow(ctx *workflow.WorkflowContext) (any, error) { + log.Println("=== OrderProcessingWorkflow STARTED ===") + + var input OrderRequest + if err := ctx.GetInput(&input); err != nil { + return nil, fmt.Errorf("failed to get workflow input: %w", err) + } + log.Printf("Processing order: %s for customer: %s", input.OrderID, input.CustomerID) + log.Printf("Full input: %+v", input) + + // Step 1: Validate Order (local activity) + var validationResult OrderValidationResult + if err := ctx.CallActivity("ValidateOrderActivity", + workflow.WithActivityInput(input)).Await(&validationResult); err != nil { + return nil, fmt.Errorf("order validation failed: %w", err) + } + + // Step 2: Process Payment (call Java payment service) + log.Println("=== STEP 2: Starting Payment Processing activity on payment-service ===") + var paymentResult string + if err := ctx.CallActivity("io.dapr.quickstarts.workflows.activities.ValidatePaymentMethodActivity", + workflow.WithActivityInput(input), + workflow.WithActivityAppID("payment-service")).Await(&paymentResult); err != nil { + log.Printf("ERROR: Payment processing failed with error: %v", err) + return nil, fmt.Errorf("payment processing failed: %w", err) + } + log.Println("=== STEP 2: Payment Processing COMPLETED ===") + + // Step 3: Reserve Inventory (call inventory-service) + log.Println("=== STEP 3: Starting Inventory Reservation activity on inventory-service ===") + var inventoryResult InventoryResult + if err := ctx.CallActivity("io.dapr.quickstarts.workflows.activities.ReserveInventoryActivity", + workflow.WithActivityInput(input), + workflow.WithActivityAppID("inventory-service")).Await(&inventoryResult); err != nil { + log.Printf("ERROR: Inventory reservation failed with error: %v", err) + return nil, fmt.Errorf("inventory reservation failed: %w", err) + } + log.Println("=== STEP 3: Inventory Reservation COMPLETED ===") + + // Step 4: Generate AI Recommendations (call AI recommendation service) + log.Println("=== STEP 4: Starting AI Recommendations activity on ai-recommendation-service ===") + var recommendationResult RecommendationResult + if err := ctx.CallActivity("io.dapr.quickstarts.workflows.activities.GeneratePersonalizedRecommendationsActivity", + workflow.WithActivityInput(input), + workflow.WithActivityAppID("ai-recommendation-service")).Await(&recommendationResult); err != nil { + log.Printf("ERROR: AI recommendations failed with error: %v", err) + return nil, fmt.Errorf("AI recommendations failed: %w", err) + } + log.Println("=== STEP 4: AI Recommendations COMPLETED ===") + + // Step 5: Complete Order (local activity) + log.Println("=== STEP 5: Starting Order Completion activity (local) ===") + var orderResult OrderResult + if err := ctx.CallActivity("CompleteOrderActivity", workflow.WithActivityInput(input)).Await(&orderResult); err != nil { + log.Printf("ERROR: Order completion failed with error: %v", err) + return nil, fmt.Errorf("order completion failed: %w", err) + } + log.Println("=== STEP 5: Order Completion COMPLETED ===") + + // Create final result with all working steps + finalResult := map[string]interface{}{ + "validation": validationResult, + "payment": paymentResult, + "inventory": inventoryResult, + "recommendations": recommendationResult, + "order": orderResult, + } + + log.Println("=== OrderProcessingWorkflow COMPLETED SUCCESSFULLY ===") + return finalResult, nil +} + +// ValidateOrderActivity validates the order locally +func ValidateOrderActivity(ctx workflow.ActivityContext) (any, error) { + log.Println("=== ValidateOrderActivity (local) STARTED ===") + + var input OrderRequest + if err := ctx.GetInput(&input); err != nil { + return nil, fmt.Errorf("failed to get activity input: %w", err) + } + log.Printf("Validating order: %s", input.OrderID) + + // Simulate validation logic + time.Sleep(1 * time.Second) + + result := OrderValidationResult{ + Valid: true, + Total: input.Total, + Message: "Order validation successful", + } + + log.Println("=== ValidateOrderActivity (local) COMPLETED ===") + return result, nil +} + +// CompleteOrderActivity completes the order locally +func CompleteOrderActivity(ctx workflow.ActivityContext) (any, error) { + log.Println("=== CompleteOrderActivity (local) STARTED ===") + + var input OrderRequest + if err := ctx.GetInput(&input); err != nil { + return nil, fmt.Errorf("failed to get activity input: %w", err) + } + log.Printf("Completing order: %s", input.OrderID) + + // Simulate order completion logic + time.Sleep(1 * time.Second) + + result := OrderResult{ + OrderID: input.OrderID, + CustomerID: input.CustomerID, + Status: "completed", + Total: input.Total, + Message: "Order completed successfully", + } + + log.Println("=== CompleteOrderActivity (local) COMPLETED ===") + return result, nil +} diff --git a/workflows/multiapp/go/order-orchestrator/models.go b/workflows/multiapp/go/order-orchestrator/models.go new file mode 100644 index 000000000..215102650 --- /dev/null +++ b/workflows/multiapp/go/order-orchestrator/models.go @@ -0,0 +1,66 @@ +package main + +// OrderRequest represents an incoming order +type OrderRequest struct { + OrderID string `json:"orderId"` + CustomerID string `json:"customerId"` + Items []Item `json:"items"` + PaymentMethod string `json:"paymentMethod"` + Total float64 `json:"total"` +} + +// CalculateTotal calculates the total price for all items in the order +func (o *OrderRequest) CalculateTotal() float64 { + total := 0.0 + for _, item := range o.Items { + total += item.Price * float64(item.Quantity) + } + o.Total = total + return total +} + +// Item represents a product in an order +type Item struct { + ProductID string `json:"productId"` + Name string `json:"name"` + Price float64 `json:"price"` + Quantity int `json:"quantity"` +} + +// OrderValidationResult represents the result of order validation +type OrderValidationResult struct { + Valid bool `json:"valid"` + Total float64 `json:"total"` + Message string `json:"message"` +} + +// InventoryResult represents the result of inventory reservation +type InventoryResult struct { + Success bool `json:"success"` + ReservedItems []Item `json:"reservedItems"` + Message string `json:"message"` +} + +// RecommendedItem represents a recommended product +type RecommendedItem struct { + ProductID string `json:"productId"` + Name string `json:"name"` + Price float64 `json:"price"` + Reason string `json:"reason"` +} + +// RecommendationResult represents the result of AI recommendations +type RecommendationResult struct { + Success bool `json:"success"` + Recommendations []RecommendedItem `json:"recommendations"` + Message string `json:"message"` +} + +// OrderResult represents the final result of order processing +type OrderResult struct { + OrderID string `json:"orderId"` + CustomerID string `json:"customerId"` + Status string `json:"status"` + Total float64 `json:"total"` + Message string `json:"message"` +} diff --git a/workflows/multiapp/java/.sdkmanrc b/workflows/multiapp/java/.sdkmanrc new file mode 100644 index 000000000..543d268ab --- /dev/null +++ b/workflows/multiapp/java/.sdkmanrc @@ -0,0 +1,4 @@ +# Enable auto-env through the sdkman_auto_env config +# Add key=value pairs of SDKs to use below +java=17.0.11-tem +maven=3.8.5 \ No newline at end of file diff --git a/workflows/multiapp/java/payment-service/pom.xml b/workflows/multiapp/java/payment-service/pom.xml new file mode 100644 index 000000000..b7ba75891 --- /dev/null +++ b/workflows/multiapp/java/payment-service/pom.xml @@ -0,0 +1,113 @@ + + + 4.0.0 + + io.dapr.quickstarts + payment-service + 1.0.0 + jar + + Payment Service + Java Payment Service for Multi-App Workflows + + + 17 + 17 + UTF-8 + 1.16.0 + + + + + + io.dapr + dapr-sdk-workflows + ${dapr.sdk.version} + + + + + io.grpc + grpc-netty-shaded + 1.73.0 + + + io.grpc + grpc-protobuf + 1.73.0 + + + io.grpc + grpc-stub + 1.73.0 + + + + + com.google.protobuf + protobuf-java + 3.25.5 + + + + + com.fasterxml.jackson.core + jackson-databind + 2.15.2 + + + + + org.slf4j + slf4j-simple + 1.7.36 + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.11.0 + + 17 + 17 + + + + org.apache.maven.plugins + maven-shade-plugin + 3.4.1 + + + package + + shade + + + + + io.dapr.quickstarts.workflows.PaymentServiceApplication + + + + + + *:* + + META-INF/*.SF + META-INF/*.DSA + META-INF/*.RSA + + + + + + + + + + diff --git a/workflows/multiapp/java/payment-service/src/main/java/io/dapr/quickstarts/workflows/PaymentServiceApplication.java b/workflows/multiapp/java/payment-service/src/main/java/io/dapr/quickstarts/workflows/PaymentServiceApplication.java new file mode 100644 index 000000000..d255c15f8 --- /dev/null +++ b/workflows/multiapp/java/payment-service/src/main/java/io/dapr/quickstarts/workflows/PaymentServiceApplication.java @@ -0,0 +1,51 @@ +package io.dapr.quickstarts.workflows; + +import io.dapr.workflows.runtime.WorkflowRuntime; +import io.dapr.workflows.runtime.WorkflowRuntimeBuilder; +import io.dapr.quickstarts.workflows.activities.ValidatePaymentMethodActivity; +import com.sun.net.httpserver.HttpServer; +import com.sun.net.httpserver.HttpHandler; +import com.sun.net.httpserver.HttpExchange; +import java.io.IOException; +import java.io.OutputStream; +import java.net.InetSocketAddress; + +/** + * PaymentServiceWorker - registers only the ValidatePaymentMethodActivity. + * This app will handle cross-app activity calls from the main workflow (written in Go). + */ +public class PaymentServiceApplication { + + public static void main(String[] args) throws Exception { + System.out.println("=== Starting PaymentServiceWorker (ValidatePaymentMethodActivity) ==="); + + // Start HTTP server on port 50002 + HttpServer server = HttpServer.create(new InetSocketAddress(50002), 0); + server.createContext("/health", new HealthHandler()); + server.setExecutor(null); + server.start(); + System.out.println("HTTP server started on port 50002"); + + WorkflowRuntimeBuilder builder = new WorkflowRuntimeBuilder() + .registerActivity(ValidatePaymentMethodActivity.class); + + // Build and start the workflow runtime + try (WorkflowRuntime runtime = builder.build()) { + System.out.println("PaymentServiceWorker started - registered ValidatePaymentMethodActivity only"); + System.out.println("Payment Service is ready to receive cross-app activity calls..."); + System.out.println("Waiting for cross-app activity calls..."); + runtime.start(); + } + } + + static class HealthHandler implements HttpHandler { + @Override + public void handle(HttpExchange exchange) throws IOException { + String response = "Payment Service is running"; + exchange.sendResponseHeaders(200, response.length()); + OutputStream os = exchange.getResponseBody(); + os.write(response.getBytes()); + os.close(); + } + } +} diff --git a/workflows/multiapp/java/payment-service/src/main/java/io/dapr/quickstarts/workflows/activities/ValidatePaymentMethodActivity.java b/workflows/multiapp/java/payment-service/src/main/java/io/dapr/quickstarts/workflows/activities/ValidatePaymentMethodActivity.java new file mode 100644 index 000000000..b69ef3b58 --- /dev/null +++ b/workflows/multiapp/java/payment-service/src/main/java/io/dapr/quickstarts/workflows/activities/ValidatePaymentMethodActivity.java @@ -0,0 +1,34 @@ +package io.dapr.quickstarts.workflows.activities; + +import io.dapr.workflows.WorkflowActivity; +import io.dapr.workflows.WorkflowActivityContext; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * ValidatePaymentMethodActivity for Payment Service - validates payment methods. + * This activity is called cross-app from the main workflow (written in Go). + */ + public class ValidatePaymentMethodActivity implements WorkflowActivity { + private static final Logger logger = LoggerFactory.getLogger(ValidatePaymentMethodActivity.class); + + @Override + public Object run(WorkflowActivityContext context) { + logger.info("=== Payment Service: ValidatePaymentMethodActivity STARTED ==="); + + try { + // Get the order input + Object inputObj = context.getInput(Object.class); + logger.info("Received order: {}", inputObj); + + // Simple payment validation logic + String result = "Payment validated successfully by payment service"; + + logger.info("=== Payment Service: ValidatePaymentMethodActivity COMPLETED SUCCESSFULLY ==="); + return result; + + } catch (Exception e) { + logger.error("ERROR in ValidatePaymentMethodActivity: {}", e.getMessage(), e); + throw e; + } + } \ No newline at end of file diff --git a/workflows/multiapp/makefile b/workflows/multiapp/makefile new file mode 100644 index 000000000..bb68a3a1d --- /dev/null +++ b/workflows/multiapp/makefile @@ -0,0 +1,84 @@ +# Multi-App E-commerce Workflow Demo +# This makefile helps you run the complete multi-app workflow scenario + +.PHONY: help build run-all run-go run-payment run-inventory run-ai clean + +# Default target +help: + @echo "Multi-App E-commerce Workflow Demo" + @echo "==================================" + @echo "" + @echo "Available targets:" + @echo " build - Build all services" + @echo " run-all - Run all services (requires 4 terminals)" + @echo " run-go - Run Go Order Orchestrator" + @echo " run-payment - Run Java Payment Service" + @echo " run-inventory - Run Java Inventory Service" + @echo " run-ai - Run Java AI Recommendation Service" + @echo " clean - Clean Java build artifacts" + @echo "" + +# Build all services +build: + @echo "Building Go Order Orchestrator..." + @bash -c 'source ~/.sdkman/bin/sdkman-init.sh && cd go && cd order-orchestrator && go mod tidy && go build -o order-orchestrator .' + @echo "Setting up Java environment..." + @bash -c 'source ~/.sdkman/bin/sdkman-init.sh && cd java && sdk env' + @echo "Building Java Payment Service..." + @bash -c 'source ~/.sdkman/bin/sdkman-init.sh && cd java && sdk env && cd payment-service && mvn clean package -q' + @echo "Building Java Inventory Service..." + @bash -c 'source ~/.sdkman/bin/sdkman-init.sh && cd java && sdk env && cd inventory-service && mvn clean package -q' + @echo "Building Java AI Recommendation Service..." + @bash -c 'source ~/.sdkman/bin/sdkman-init.sh && cd java && sdk env && cd ai-recommendation-service && mvn clean package -q' + @echo "All services built successfully!" + +# Run all services (requires 4 terminals) +run-all: + @echo "Starting all services..." + @echo "Please open 4 terminals and run:" + @echo " Terminal 1: make run-go" + @echo " Terminal 2: make run-payment" + @echo " Terminal 3: make run-inventory" + @echo " Terminal 4: make run-ai" + @echo "" + @echo "Or run them individually:" + @echo " make run-go" + @echo " make run-payment" + @echo " make run-inventory" + @echo " make run-ai" + +# Run Go Order Orchestrator +run-go: + @echo "Starting Go Order Orchestrator..." + cd go/order-orchestrator && dapr run --log-level debug --app-id order-orchestrator --app-port 50001 --dapr-http-port 3505 --dapr-grpc-port 50014 --resources-path ../../components -- go run . + +# Run Java Payment Service +run-payment: + @echo "Starting Java Payment Service..." + @bash -c 'source ~/.sdkman/bin/sdkman-init.sh && cd java && sdk env && cd payment-service && dapr run --log-level debug --app-id payment-service --app-port 50002 --dapr-http-port 3506 --dapr-grpc-port 50015 --resources-path ../../components -- java -jar target/payment-service-1.0.0.jar' + +# Run Java Inventory Service +run-inventory: + @echo "Starting Java Inventory Service..." + @bash -c 'source ~/.sdkman/bin/sdkman-init.sh && cd java && sdk env && cd inventory-service && dapr run --log-level debug --app-id inventory-service --app-port 50003 --dapr-http-port 3507 --dapr-grpc-port 50016 --resources-path ../../components -- java -jar target/inventory-service-1.0.0.jar' + +# Run Java AI Recommendation Service +run-ai: + @echo "Starting Java AI Recommendation Service..." + @bash -c 'source ~/.sdkman/bin/sdkman-init.sh && cd java && sdk env && cd ai-recommendation-service && dapr run --log-level debug --app-id ai-recommendation-service --app-port 50004 --dapr-http-port 3508 --dapr-grpc-port 50017 --resources-path ../../components -- java -jar target/ai-recommendation-service-1.0.0.jar' + +# Clean Java build artifacts +clean: + @echo "Cleaning Java build artifacts..." + cd java/payment-service && mvn clean -q + cd java/inventory-service && mvn clean -q + cd java/ai-recommendation-service && mvn clean -q + @echo "Clean completed!" + +# Test the workflow +test: + @echo "Testing the multi-app workflow..." + @echo "Sending POST request to: http://localhost:50001/start-workflow" + @curl -X POST http://localhost:50001/start-workflow + @echo "" + @echo "Test completed!" From ff122d109233265fd1af45de0f69933c1fe5e47f Mon Sep 17 00:00:00 2001 From: Cassandra Coyle Date: Tue, 23 Sep 2025 12:15:48 -0500 Subject: [PATCH 2/5] add ai service Signed-off-by: Cassandra Coyle --- .../AIRecommendationServiceApplication.java | 51 ++++++++++++++ ...tePersonalizedRecommendationsActivity.java | 66 +++++++++++++++++++ .../multiapp/java/payment-service/pom.xml | 15 +---- .../workflows/PaymentServiceApplication.java | 4 +- .../ValidatePaymentMethodActivity.java | 8 +-- workflows/multiapp/makefile | 8 +-- 6 files changed, 127 insertions(+), 25 deletions(-) create mode 100644 workflows/multiapp/java/ai-recommendation-service/src/main/java/io/dapr/quickstarts/workflows/AIRecommendationServiceApplication.java create mode 100644 workflows/multiapp/java/ai-recommendation-service/src/main/java/io/dapr/quickstarts/workflows/activities/GeneratePersonalizedRecommendationsActivity.java diff --git a/workflows/multiapp/java/ai-recommendation-service/src/main/java/io/dapr/quickstarts/workflows/AIRecommendationServiceApplication.java b/workflows/multiapp/java/ai-recommendation-service/src/main/java/io/dapr/quickstarts/workflows/AIRecommendationServiceApplication.java new file mode 100644 index 000000000..2a67321d9 --- /dev/null +++ b/workflows/multiapp/java/ai-recommendation-service/src/main/java/io/dapr/quickstarts/workflows/AIRecommendationServiceApplication.java @@ -0,0 +1,51 @@ +package io.dapr.quickstarts.workflows; + +import io.dapr.workflows.runtime.WorkflowRuntime; +import io.dapr.workflows.runtime.WorkflowRuntimeBuilder; +import io.dapr.quickstarts.workflows.activities.GeneratePersonalizedRecommendationsActivity; +import com.sun.net.httpserver.HttpServer; +import com.sun.net.httpserver.HttpHandler; +import com.sun.net.httpserver.HttpExchange; +import java.io.IOException; +import java.io.OutputStream; +import java.net.InetSocketAddress; + +/** + * AIRecommendationServiceWorker - registers only the GeneratePersonalizedRecommendationsActivity. + * This activity is called cross-app from the main workflow (written in Go). + */ +public class AIRecommendationServiceApplication { + + public static void main(String[] args) throws Exception { + System.out.println("=== Starting AIRecommendationServiceWorker (GeneratePersonalizedRecommendationsActivity) ==="); + + // Start HTTP server on port 50004 + HttpServer server = HttpServer.create(new InetSocketAddress(50004), 0); + server.createContext("/health", new HealthHandler()); + server.setExecutor(null); + server.start(); + System.out.println("HTTP server started on port 50004"); + + WorkflowRuntimeBuilder builder = new WorkflowRuntimeBuilder() + .registerActivity(GeneratePersonalizedRecommendationsActivity.class); + + // Build and start the workflow runtime + try (WorkflowRuntime runtime = builder.build()) { + System.out.println("AIRecommendationServiceWorker started - registered GeneratePersonalizedRecommendationsActivity only"); + System.out.println("AI Recommendation Service is ready to receive multi-app activity calls..."); + System.out.println("Waiting for multi-app activity calls..."); + runtime.start(); + } + } + + static class HealthHandler implements HttpHandler { + @Override + public void handle(HttpExchange exchange) throws IOException { + String response = "AI Recommendation Service is running"; + exchange.sendResponseHeaders(200, response.length()); + OutputStream os = exchange.getResponseBody(); + os.write(response.getBytes()); + os.close(); + } + } +} diff --git a/workflows/multiapp/java/ai-recommendation-service/src/main/java/io/dapr/quickstarts/workflows/activities/GeneratePersonalizedRecommendationsActivity.java b/workflows/multiapp/java/ai-recommendation-service/src/main/java/io/dapr/quickstarts/workflows/activities/GeneratePersonalizedRecommendationsActivity.java new file mode 100644 index 000000000..1a636627c --- /dev/null +++ b/workflows/multiapp/java/ai-recommendation-service/src/main/java/io/dapr/quickstarts/workflows/activities/GeneratePersonalizedRecommendationsActivity.java @@ -0,0 +1,66 @@ +package io.dapr.quickstarts.workflows.activities; + +import io.dapr.workflows.WorkflowActivity; +import io.dapr.workflows.WorkflowActivityContext; + +/** + * GeneratePersonalizedRecommendationsActivity for AI Recommendation Service - generates AI recommendations. + * This activity is called cross-app from the main workflow. + */ +public class GeneratePersonalizedRecommendationsActivity implements WorkflowActivity { + @Override + public Object run(WorkflowActivityContext context) { + var logger = context.getLogger(); + logger.info("=== AI Recommendation Service: GeneratePersonalizedRecommendationsActivity STARTED ==="); + + try { + Object inputObj = context.getInput(Object.class); + logger.info("Received order: {}", inputObj); + + // Create RecommendationResult object + RecommendationResult result = new RecommendationResult(); + result.success = true; + result.recommendations = new RecommendedItem[]{ + new RecommendedItem("PROD-003", "Wireless Headphones", 99.99, "Based on your laptop purchase"), + new RecommendedItem("PROD-004", "USB-C Hub", 49.99, "Complements your laptop setup") + }; + result.message = "AI recommendations generated successfully by AI recommendation service"; + logger.info("=== AI Recommendation Service: GeneratePersonalizedRecommendationsActivity COMPLETED SUCCESSFULLY ==="); + + return result; + } catch (Exception e) { + logger.error("ERROR in GeneratePersonalizedRecommendationsActivity: {}", e.getMessage(), e); + throw e; + } + } + + // RecommendationResult class (to match Go struct) + public static class RecommendationResult { + public boolean success; + public RecommendedItem[] recommendations; + public String message; + + @Override + public String toString() { + return String.format("RecommendationResult{success=%s, recommendations=%d items, message='%s'}", + success, recommendations != null ? recommendations.length : 0, message); + } + } + + // RecommendedItem class (to match Go struct) + public static class RecommendedItem { + public String productId; + public String name; + public double price; + public String reason; + + public RecommendedItem() {} + + public RecommendedItem(String productId, String name, double price, String reason) { + this.productId = productId; + this.name = name; + this.price = price; + this.reason = reason; + } + } +} diff --git a/workflows/multiapp/java/payment-service/pom.xml b/workflows/multiapp/java/payment-service/pom.xml index b7ba75891..debaa22e2 100644 --- a/workflows/multiapp/java/payment-service/pom.xml +++ b/workflows/multiapp/java/payment-service/pom.xml @@ -26,13 +26,7 @@ dapr-sdk-workflows ${dapr.sdk.version} - - - - io.grpc - grpc-netty-shaded - 1.73.0 - + io.grpc grpc-protobuf @@ -51,13 +45,6 @@ 3.25.5 - - - com.fasterxml.jackson.core - jackson-databind - 2.15.2 - - org.slf4j diff --git a/workflows/multiapp/java/payment-service/src/main/java/io/dapr/quickstarts/workflows/PaymentServiceApplication.java b/workflows/multiapp/java/payment-service/src/main/java/io/dapr/quickstarts/workflows/PaymentServiceApplication.java index d255c15f8..b134fad3d 100644 --- a/workflows/multiapp/java/payment-service/src/main/java/io/dapr/quickstarts/workflows/PaymentServiceApplication.java +++ b/workflows/multiapp/java/payment-service/src/main/java/io/dapr/quickstarts/workflows/PaymentServiceApplication.java @@ -32,8 +32,8 @@ public static void main(String[] args) throws Exception { // Build and start the workflow runtime try (WorkflowRuntime runtime = builder.build()) { System.out.println("PaymentServiceWorker started - registered ValidatePaymentMethodActivity only"); - System.out.println("Payment Service is ready to receive cross-app activity calls..."); - System.out.println("Waiting for cross-app activity calls..."); + System.out.println("Payment Service is ready to receive multi-app activity calls..."); + System.out.println("Waiting for multi-app activity calls..."); runtime.start(); } } diff --git a/workflows/multiapp/java/payment-service/src/main/java/io/dapr/quickstarts/workflows/activities/ValidatePaymentMethodActivity.java b/workflows/multiapp/java/payment-service/src/main/java/io/dapr/quickstarts/workflows/activities/ValidatePaymentMethodActivity.java index b69ef3b58..9e295bec5 100644 --- a/workflows/multiapp/java/payment-service/src/main/java/io/dapr/quickstarts/workflows/activities/ValidatePaymentMethodActivity.java +++ b/workflows/multiapp/java/payment-service/src/main/java/io/dapr/quickstarts/workflows/activities/ValidatePaymentMethodActivity.java @@ -2,18 +2,15 @@ import io.dapr.workflows.WorkflowActivity; import io.dapr.workflows.WorkflowActivityContext; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; /** * ValidatePaymentMethodActivity for Payment Service - validates payment methods. * This activity is called cross-app from the main workflow (written in Go). */ public class ValidatePaymentMethodActivity implements WorkflowActivity { - private static final Logger logger = LoggerFactory.getLogger(ValidatePaymentMethodActivity.class); - @Override public Object run(WorkflowActivityContext context) { + var logger = context.getLogger(); logger.info("=== Payment Service: ValidatePaymentMethodActivity STARTED ==="); try { @@ -31,4 +28,5 @@ public Object run(WorkflowActivityContext context) { logger.error("ERROR in ValidatePaymentMethodActivity: {}", e.getMessage(), e); throw e; } - } \ No newline at end of file + } + } \ No newline at end of file diff --git a/workflows/multiapp/makefile b/workflows/multiapp/makefile index bb68a3a1d..7d5f4f2db 100644 --- a/workflows/multiapp/makefile +++ b/workflows/multiapp/makefile @@ -50,22 +50,22 @@ run-all: # Run Go Order Orchestrator run-go: @echo "Starting Go Order Orchestrator..." - cd go/order-orchestrator && dapr run --log-level debug --app-id order-orchestrator --app-port 50001 --dapr-http-port 3505 --dapr-grpc-port 50014 --resources-path ../../components -- go run . + cd go/order-orchestrator && dapr run --app-id order-orchestrator --app-port 50001 --dapr-http-port 3505 --dapr-grpc-port 50014 --resources-path ../../components -- go run . # Run Java Payment Service run-payment: @echo "Starting Java Payment Service..." - @bash -c 'source ~/.sdkman/bin/sdkman-init.sh && cd java && sdk env && cd payment-service && dapr run --log-level debug --app-id payment-service --app-port 50002 --dapr-http-port 3506 --dapr-grpc-port 50015 --resources-path ../../components -- java -jar target/payment-service-1.0.0.jar' + @bash -c 'source ~/.sdkman/bin/sdkman-init.sh && cd java && sdk env && cd payment-service && dapr run --app-id payment-service --app-port 50002 --dapr-http-port 3506 --dapr-grpc-port 50015 --resources-path ../../components -- java -jar target/payment-service-1.0.0.jar' # Run Java Inventory Service run-inventory: @echo "Starting Java Inventory Service..." - @bash -c 'source ~/.sdkman/bin/sdkman-init.sh && cd java && sdk env && cd inventory-service && dapr run --log-level debug --app-id inventory-service --app-port 50003 --dapr-http-port 3507 --dapr-grpc-port 50016 --resources-path ../../components -- java -jar target/inventory-service-1.0.0.jar' + @bash -c 'source ~/.sdkman/bin/sdkman-init.sh && cd java && sdk env && cd inventory-service && dapr run --app-id inventory-service --app-port 50003 --dapr-http-port 3507 --dapr-grpc-port 50016 --resources-path ../../components -- java -jar target/inventory-service-1.0.0.jar' # Run Java AI Recommendation Service run-ai: @echo "Starting Java AI Recommendation Service..." - @bash -c 'source ~/.sdkman/bin/sdkman-init.sh && cd java && sdk env && cd ai-recommendation-service && dapr run --log-level debug --app-id ai-recommendation-service --app-port 50004 --dapr-http-port 3508 --dapr-grpc-port 50017 --resources-path ../../components -- java -jar target/ai-recommendation-service-1.0.0.jar' + @bash -c 'source ~/.sdkman/bin/sdkman-init.sh && cd java && sdk env && cd ai-recommendation-service && dapr run --app-id ai-recommendation-service --app-port 50004 --dapr-http-port 3508 --dapr-grpc-port 50017 --resources-path ../../components -- java -jar target/ai-recommendation-service-1.0.0.jar' # Clean Java build artifacts clean: From d2cc18d250b19e14a55c7b7ba9b61884768adf80 Mon Sep 17 00:00:00 2001 From: Cassandra Coyle Date: Tue, 23 Sep 2025 13:52:47 -0500 Subject: [PATCH 3/5] cleanup and add inventory service Signed-off-by: Cassandra Coyle --- .../java/ai-recommendation-service/pom.xml | 75 +++++++++++++++++++ .../AIRecommendationServiceApplication.java | 2 +- .../multiapp/java/inventory-service/pom.xml | 75 +++++++++++++++++++ .../InventoryServiceApplication.java | 52 +++++++++++++ .../activities/ReserveInventoryActivity.java | 54 +++++++++++++ .../multiapp/java/payment-service/pom.xml | 25 ------- workflows/multiapp/makefile | 6 +- 7 files changed, 260 insertions(+), 29 deletions(-) create mode 100644 workflows/multiapp/java/ai-recommendation-service/pom.xml create mode 100644 workflows/multiapp/java/inventory-service/pom.xml create mode 100644 workflows/multiapp/java/inventory-service/src/main/java/io/dapr/quickstarts/workflows/InventoryServiceApplication.java create mode 100644 workflows/multiapp/java/inventory-service/src/main/java/io/dapr/quickstarts/workflows/activities/ReserveInventoryActivity.java diff --git a/workflows/multiapp/java/ai-recommendation-service/pom.xml b/workflows/multiapp/java/ai-recommendation-service/pom.xml new file mode 100644 index 000000000..2c773818e --- /dev/null +++ b/workflows/multiapp/java/ai-recommendation-service/pom.xml @@ -0,0 +1,75 @@ + + + 4.0.0 + + io.dapr.quickstarts + ai-recommendation-service + 1.0.0 + jar + + AI Recommendation Service + Java AI Recommendation Service for Multi-App Workflows + + + 17 + 17 + UTF-8 + 1.16.0 + + + + + + io.dapr + dapr-sdk-workflows + ${dapr.sdk.version} + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.11.0 + + 17 + 17 + + + + org.apache.maven.plugins + maven-shade-plugin + 3.4.1 + + + package + + shade + + + + + io.dapr.quickstarts.workflows.AIRecommendationServiceApplication + + + + + + *:* + + META-INF/*.SF + META-INF/*.DSA + META-INF/*.RSA + + + + + + + + + + diff --git a/workflows/multiapp/java/ai-recommendation-service/src/main/java/io/dapr/quickstarts/workflows/AIRecommendationServiceApplication.java b/workflows/multiapp/java/ai-recommendation-service/src/main/java/io/dapr/quickstarts/workflows/AIRecommendationServiceApplication.java index 2a67321d9..dd8c99ea6 100644 --- a/workflows/multiapp/java/ai-recommendation-service/src/main/java/io/dapr/quickstarts/workflows/AIRecommendationServiceApplication.java +++ b/workflows/multiapp/java/ai-recommendation-service/src/main/java/io/dapr/quickstarts/workflows/AIRecommendationServiceApplication.java @@ -12,7 +12,7 @@ /** * AIRecommendationServiceWorker - registers only the GeneratePersonalizedRecommendationsActivity. - * This activity is called cross-app from the main workflow (written in Go). + * This activity is called multi-app from the main workflow (written in Go). */ public class AIRecommendationServiceApplication { diff --git a/workflows/multiapp/java/inventory-service/pom.xml b/workflows/multiapp/java/inventory-service/pom.xml new file mode 100644 index 000000000..bf5401831 --- /dev/null +++ b/workflows/multiapp/java/inventory-service/pom.xml @@ -0,0 +1,75 @@ + + + 4.0.0 + + io.dapr.quickstarts + inventory-service + 1.0.0 + jar + + Inventory Service + Java Inventory Service for Multi-App Workflows + + + 17 + 17 + UTF-8 + 1.16.0 + + + + + + io.dapr + dapr-sdk-workflows + ${dapr.sdk.version} + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.11.0 + + 17 + 17 + + + + org.apache.maven.plugins + maven-shade-plugin + 3.4.1 + + + package + + shade + + + + + io.dapr.quickstarts.workflows.InventoryServiceApplication + + + + + + *:* + + META-INF/*.SF + META-INF/*.DSA + META-INF/*.RSA + + + + + + + + + + \ No newline at end of file diff --git a/workflows/multiapp/java/inventory-service/src/main/java/io/dapr/quickstarts/workflows/InventoryServiceApplication.java b/workflows/multiapp/java/inventory-service/src/main/java/io/dapr/quickstarts/workflows/InventoryServiceApplication.java new file mode 100644 index 000000000..80a5056ff --- /dev/null +++ b/workflows/multiapp/java/inventory-service/src/main/java/io/dapr/quickstarts/workflows/InventoryServiceApplication.java @@ -0,0 +1,52 @@ +package io.dapr.quickstarts.workflows; + +import io.dapr.workflows.runtime.WorkflowRuntime; +import io.dapr.workflows.runtime.WorkflowRuntimeBuilder; +import io.dapr.quickstarts.workflows.activities.ReserveInventoryActivity; +import com.sun.net.httpserver.HttpServer; +import com.sun.net.httpserver.HttpHandler; +import com.sun.net.httpserver.HttpExchange; +import java.io.IOException; +import java.io.OutputStream; +import java.net.InetSocketAddress; + +/** + * InventoryServiceWorker - registers only the ReserveInventoryActivity. + * This app will handle multi-app activity calls from the main workflow. + */ +public class InventoryServiceApplication { + + public static void main(String[] args) throws Exception { + System.out.println("=== Starting InventoryServiceWorker (ReserveInventoryActivity) ==="); + + // Start HTTP server on port 50003 + HttpServer server = HttpServer.create(new InetSocketAddress(50003), 0); + server.createContext("/health", new HealthHandler()); + server.setExecutor(null); + server.start(); + System.out.println("HTTP server started on port 50003"); + + WorkflowRuntimeBuilder builder = new WorkflowRuntimeBuilder() + .registerActivity(ReserveInventoryActivity.class); + + // Build and start the workflow runtime + try (WorkflowRuntime runtime = builder.build()) { + System.out.println("InventoryServiceWorker started - registered ReserveInventoryActivity only"); + System.out.println("Inventory Service is ready to receive cross-app activity calls..."); + System.out.println("Waiting for cross-app activity calls..."); + runtime.start(); + } + } + + static class HealthHandler implements HttpHandler { + @Override + public void handle(HttpExchange exchange) throws IOException { + String response = "Inventory Service is running"; + exchange.sendResponseHeaders(200, response.length()); + OutputStream os = exchange.getResponseBody(); + os.write(response.getBytes()); + os.close(); + } + } + +} diff --git a/workflows/multiapp/java/inventory-service/src/main/java/io/dapr/quickstarts/workflows/activities/ReserveInventoryActivity.java b/workflows/multiapp/java/inventory-service/src/main/java/io/dapr/quickstarts/workflows/activities/ReserveInventoryActivity.java new file mode 100644 index 000000000..676d6305e --- /dev/null +++ b/workflows/multiapp/java/inventory-service/src/main/java/io/dapr/quickstarts/workflows/activities/ReserveInventoryActivity.java @@ -0,0 +1,54 @@ +package io.dapr.quickstarts.workflows.activities; + +import io.dapr.workflows.WorkflowActivity; +import io.dapr.workflows.WorkflowActivityContext; + +/** + * ReserveInventoryActivity for Inventory Service - reserves inventory items. + * This activity is called multi-app from the main workflow. + */ +public class ReserveInventoryActivity implements WorkflowActivity { + @Override + public Object run(WorkflowActivityContext context) { + var logger = context.getLogger(); + logger.info("=== Inventory Service: ReserveInventoryActivity STARTED ==="); + + try { + Object inputObj = context.getInput(Object.class); + logger.info("Received order: {}", inputObj); + + // Create InventoryResult object + InventoryResult result = new InventoryResult(); + result.success = true; + result.reservedItems = new Item[0]; + result.message = "Inventory reserved successfully by inventory service"; + logger.info("=== Inventory Service: ReserveInventoryActivity COMPLETED SUCCESSFULLY ==="); + + return result; + } catch (Exception e) { + logger.error("ERROR in ReserveInventoryActivity: {}", e.getMessage(), e); + throw e; + } + } + + // InventoryResult class (to match Go struct) + public static class InventoryResult { + public boolean success; + public Item[] reservedItems; + public String message; + + @Override + public String toString() { + return String.format("InventoryResult{success=%s, reservedItems=%d items, message='%s'}", + success, reservedItems != null ? reservedItems.length : 0, message); + } + } + + // Item class (to match Go struct) + public static class Item { + public String productId; + public String name; + public double price; + public int quantity; + } +} diff --git a/workflows/multiapp/java/payment-service/pom.xml b/workflows/multiapp/java/payment-service/pom.xml index debaa22e2..ea9e880b5 100644 --- a/workflows/multiapp/java/payment-service/pom.xml +++ b/workflows/multiapp/java/payment-service/pom.xml @@ -26,31 +26,6 @@ dapr-sdk-workflows ${dapr.sdk.version} - - - io.grpc - grpc-protobuf - 1.73.0 - - - io.grpc - grpc-stub - 1.73.0 - - - - - com.google.protobuf - protobuf-java - 3.25.5 - - - - - org.slf4j - slf4j-simple - 1.7.36 - diff --git a/workflows/multiapp/makefile b/workflows/multiapp/makefile index 7d5f4f2db..d2f95982c 100644 --- a/workflows/multiapp/makefile +++ b/workflows/multiapp/makefile @@ -70,9 +70,9 @@ run-ai: # Clean Java build artifacts clean: @echo "Cleaning Java build artifacts..." - cd java/payment-service && mvn clean -q - cd java/inventory-service && mvn clean -q - cd java/ai-recommendation-service && mvn clean -q + @bash -c 'source ~/.sdkman/bin/sdkman-init.sh && cd java && sdk env && cd payment-service && mvn clean compile -q' + @bash -c 'source ~/.sdkman/bin/sdkman-init.sh && cd java && sdk env && cd inventory-service && mvn clean compile -q' + @bash -c 'source ~/.sdkman/bin/sdkman-init.sh && cd java && sdk env && cd ai-recommendation-service && mvn clean compile -q' @echo "Clean completed!" # Test the workflow From bc9a553bb202e7648ae8a636f609cfe8e60c4788 Mon Sep 17 00:00:00 2001 From: Cassandra Coyle Date: Tue, 23 Sep 2025 14:23:53 -0500 Subject: [PATCH 4/5] add multiapp run file Signed-off-by: Cassandra Coyle --- workflows/multiapp/dapr.yaml | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 workflows/multiapp/dapr.yaml diff --git a/workflows/multiapp/dapr.yaml b/workflows/multiapp/dapr.yaml new file mode 100644 index 000000000..b33d24198 --- /dev/null +++ b/workflows/multiapp/dapr.yaml @@ -0,0 +1,28 @@ +version: 1 +common: + resourcesPath: ./components +apps: + - appID: order-orchestrator + appDirPath: ./go/order-orchestrator/ + appPort: 50001 + daprHTTPPort: 3505 + daprGRPCPort: 50014 + command: ["go", "run", "."] + - appID: payment-service + appDirPath: ./java/payment-service/ + appPort: 50002 + daprHTTPPort: 3506 + daprGRPCPort: 50015 + command: ["java", "-jar", "target/payment-service-1.0.0.jar"] + - appID: inventory-service + appDirPath: ./java/inventory-service/ + appPort: 50003 + daprHTTPPort: 3507 + daprGRPCPort: 50016 + command: ["java", "-jar", "target/inventory-service-1.0.0.jar"] + - appID: ai-recommendation-service + appDirPath: ./java/ai-recommendation-service/ + appPort: 50004 + daprHTTPPort: 3508 + daprGRPCPort: 50017 + command: ["java", "-jar", "target/ai-recommendation-service-1.0.0.jar"] From d854bbd43e72a7438c26f8c76643ae562f66e44a Mon Sep 17 00:00:00 2001 From: Cassandra Coyle Date: Tue, 23 Sep 2025 14:38:31 -0500 Subject: [PATCH 5/5] add readme Signed-off-by: Cassandra Coyle --- workflows/multiapp/README.md | 117 +++++++++++++++++++++++++++++++++++ 1 file changed, 117 insertions(+) create mode 100644 workflows/multiapp/README.md diff --git a/workflows/multiapp/README.md b/workflows/multiapp/README.md new file mode 100644 index 000000000..edd822d1c --- /dev/null +++ b/workflows/multiapp/README.md @@ -0,0 +1,117 @@ +# Multi-App E-commerce Workflow Demo + +This demo showcases a realistic e-commerce order processing scenario using Dapr's multi-application workflow capabilities. The scenario demonstrates how a Go application can orchestrate complex business processes across multiple Java services, including AI-powered recommendations. + +## Architecture + +![E-commerce Architecture](E-CommerceArchitecture.png) + +The demo consists of four applications working together: + +1. **Go Order Orchestrator** - Main workflow coordinator +2. **Java Payment Service** - Payment processing +3. **Java Inventory Service** - Inventory management and reservation +4. **Java AI Recommendation Service** - AI-powered product recommendations + +## Prerequisites + +- Dapr CLI installed and initialized (`dapr init`) +- Go installed +- Java and Maven installed + +## Quick Start + +### 1. Build All Services + +```bash +make build +``` + +### 2. Run All Services + +#### Option A: Run with Multi-App Configuration (Recommended) + +```bash +dapr run -f dapr.yaml +``` + +#### Option B: Run in Separate Terminals + +**Terminal 1 - Go Order Orchestrator:** +```bash +make run-go +``` + +**Terminal 2 - Java Payment Service:** +```bash +make run-payment +``` + +**Terminal 3 - Java Inventory Service:** +```bash +make run-inventory +``` + +**Terminal 4 - Java AI Recommendation Service:** +```bash +make run-ai +``` + +### 3. Test the Workflow + +Send a POST request to start the workflow: + +```bash +curl -X POST http://localhost:50001/start-workflow +``` + +## Service Ports + +- **Go Order Orchestrator**: 50001 (Dapr HTTP: 3505) +- **Java Payment Service**: 50002 (Dapr HTTP: 3506) +- **Java Inventory Service**: 50003 (Dapr HTTP: 3507) +- **Java AI Recommendation Service**: 50004 (Dapr HTTP: 3508) + +## Development + +### Building Individual Services + +```bash +# Go Order Orchestrator +cd go/order-orchestrator +go mod tidy +go run . + +# Java Payment Service +cd java/payment-service +mvn clean package +java -jar target/payment-service-1.0.0.jar + +# Java Inventory Service +cd java/inventory-service +mvn clean package +java -jar target/inventory-service-1.0.0.jar + +# Java AI Recommendation Service +cd java/ai-recommendation-service +mvn clean package +java -jar target/ai-recommendation-service-1.0.0.jar +``` + +### Testing + +```bash +make test +``` + +### Cleanup + +```bash +make clean +``` + +## Related Resources + +- [Multi-Application Workflows Documentation](https://docs.dapr.io/developing-applications/building-blocks/workflow/workflow-multi-app/) +- [Multi-Application Java Workflows Example](https://github.com/dapr/java-sdk/tree/master/examples/src/main/java/io/dapr/examples/workflows/multiapp) +- [Multi-Application Spring Boot Workflows Example](https://github.com/dapr/java-sdk/tree/master/spring-boot-examples/workflows/multi-app)