{"id":515,"date":"2024-11-20T14:30:50","date_gmt":"2024-11-20T14:30:50","guid":{"rendered":"https:\/\/corneliadavis.com\/blog\/?p=515"},"modified":"2024-11-20T14:34:28","modified_gmt":"2024-11-20T14:34:28","slug":"temporal-activities-as-spring-beans-an-addendum","status":"publish","type":"post","link":"https:\/\/corneliadavis.com\/blog\/2024\/11\/20\/temporal-activities-as-spring-beans-an-addendum\/","title":{"rendered":"Temporal Activities as Spring Beans (an Addendum)"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">This is a quick little addendum to <a href=\"https:\/\/corneliadavis.com\/blog\/2024\/11\/13\/spring-boot-and-temporal\/\" target=\"_blank\" rel=\"noreferrer noopener\">last week\u2019s post on Spring and Temporal<\/a>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">In last week\u2019s post I covered a range of different ways to use the Spring Framework with Temporal that included leveraging abstractions for making HTTP calls, and using the Spring Boot Maven plugin to build fat jars. But the topic that offered the most uncertainty (when I started the exercise) was making the Temporal worker a Spring Boot application, and allowing various portions of the implementation to be Spring beans. I concluded that workflows should NOT be Spring beans, but that the main worker application and activities COULD be Spring beans.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">But the <a href=\"https:\/\/github.com\/cdavisafc\/cloudnativepatterns\/tree\/temporal-spring-v1.0\/cloudnative-statelessness-temporal\/cloudnative-connectionposts\/workflow\" rel=\"noreferrer noopener\" target=\"_blank\">implementation<\/a> that I offered, and drew a diagram for, only made the <em>worker<\/em> a bean, not the activities, and while this was fine, it was a bit awkward. The awkwardness comes from the fact that I injected configuration into the worker, specifically into the <code><a href=\"https:\/\/github.com\/cdavisafc\/cloudnativepatterns\/blob\/temporal-spring-v1.0\/cloudnative-statelessness-temporal\/cloudnative-connectionposts\/workflow\/src\/main\/java\/com\/corneliadavis\/cloudnative\/connectionsposts\/workflow\/PostAggregatorWorkerConfig.java#L47\" rel=\"noreferrer noopener\" target=\"_blank\">PostAggregatorWorkerConfig<\/a><\/code>, only to pass it over to the activity via the constructor.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Injecting the property values directly into the activities implementation would be more elegant, after all, that is where they are used. To use Spring to inject that configuration, however, meant that I\u2019d need to make the Activities implementation class a Spring Bean. As I said, I had reached the conclusion that this worked, so I made changes in two places:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">First, in the <code>PostAggregatorActivitiesImpl<\/code> class we do two things:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img data-recalc-dims=\"1\" decoding=\"async\" loading=\"lazy\" src=\"https:\/\/i0.wp.com\/cdn-images-1.medium.com\/max\/800\/1%2AvjcjHIhb9u02Br9b1jbcdw.png?w=767&#038;ssl=1\" alt=\"\"\/><\/figure>\n\n\n\n<ol class=\"wp-block-list\">\n<li>By adding the <code>@Component<\/code> annotation to the PostAggregatorActivitiesImpl class, Spring will now create an instance of this class; that is, it creates a Spring bean.<\/li>\n\n\n\n<li>And because Spring is managing the instance, property values can now be injected into the instance via <code>@Value<\/code> annotations.<\/li>\n<\/ol>\n\n\n\n<p class=\"wp-block-paragraph\">Then, in the <code>PostAggregatorWorkerConfig<\/code> we do two things:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img data-recalc-dims=\"1\" decoding=\"async\" loading=\"lazy\" src=\"https:\/\/i0.wp.com\/cdn-images-1.medium.com\/max\/800\/1%2AaT6jfr3Sh_10lnp_Q6TJLg.png?w=767&#038;ssl=1\" alt=\"\"\/><\/figure>\n\n\n\n<ol class=\"wp-block-list\">\n<li>We add a <code>PostAggregatorActivitiesImpl<\/code> parameter into the method that creates the worker bean. Spring will auto-wire the bean it created as a result of the <code>@Component<\/code> annotation.<\/li>\n\n\n\n<li>And then we register that Activity implementation instance with the worker that is created. Note that in the original implementation, WE were lifecycle managing the Activities instance\u200a\u2014\u200awe called <code>new<\/code> when registering the Activities implementation. Now Spring is lifecycle managing it for us.<\/li>\n<\/ol>\n\n\n\n<p class=\"wp-block-paragraph\">Here\u2019s an updated diagram:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img data-recalc-dims=\"1\" decoding=\"async\" loading=\"lazy\" src=\"https:\/\/i0.wp.com\/cdn-images-1.medium.com\/max\/800\/1%2A7sTbmzP9as1ZRMwtSMCudg.png?w=767&#038;ssl=1\" alt=\"\"\/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">I like this new approach better and you\u2019ll find that <a href=\"https:\/\/github.com\/cdavisafc\/cloudnativepatterns\/tree\/temporal-spring-v2.0\/cloudnative-statelessness-temporal\/cloudnative-connectionposts\/workflow\" rel=\"noreferrer noopener\" target=\"_blank\">implementation here<\/a>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">As always, would love to hear from you if you found something helpful, and\/or have suggestions for improvement.<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img data-recalc-dims=\"1\" decoding=\"async\" loading=\"lazy\" src=\"https:\/\/i0.wp.com\/cdn-images-1.medium.com\/max\/800\/1%2AIzxlI3gwNf9seMM2Q_c7pQ.png?w=767&#038;ssl=1\" alt=\"\"\/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\"><em>This has been cross posted on <a href=\"https:\/\/blog.corneliadavis.com\/temporal-activities-as-spring-beans-an-addendum-f9f72418cd79\">Medium<\/a> as well<\/em><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n","protected":false},"excerpt":{"rendered":"<p>This is a quick little addendum to last week\u2019s post on Spring and Temporal. In last week\u2019s post I covered a range of different ways to use the Spring Framework with Temporal that included leveraging abstractions for making HTTP calls, and using the Spring Boot Maven plugin to build fat jars. But the topic that [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"nf_dc_page":"","_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[1],"tags":[111,112,107],"class_list":["post-515","post","type-post","status-publish","format-standard","hentry","category-uncategorized","tag-durable-execution","tag-spring-boot","tag-temporal"],"jetpack_featured_media_url":"","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/corneliadavis.com\/blog\/wp-json\/wp\/v2\/posts\/515","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/corneliadavis.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/corneliadavis.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/corneliadavis.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/corneliadavis.com\/blog\/wp-json\/wp\/v2\/comments?post=515"}],"version-history":[{"count":2,"href":"https:\/\/corneliadavis.com\/blog\/wp-json\/wp\/v2\/posts\/515\/revisions"}],"predecessor-version":[{"id":517,"href":"https:\/\/corneliadavis.com\/blog\/wp-json\/wp\/v2\/posts\/515\/revisions\/517"}],"wp:attachment":[{"href":"https:\/\/corneliadavis.com\/blog\/wp-json\/wp\/v2\/media?parent=515"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/corneliadavis.com\/blog\/wp-json\/wp\/v2\/categories?post=515"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/corneliadavis.com\/blog\/wp-json\/wp\/v2\/tags?post=515"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}