This article is going to focus on invoicing clients for services that have been performed and will utilize messaging solutions within the Heroku ecosystem. The goals of the invoice process are as follows: Quick and easy to use Accurate and allow adjustments Notify clients via SMS, including a link to an online invoice Allow clients to submit payments using Venmo Below, is a current version of the feature roadmap: Invoice Processing and Flow In order to illustrate the flow for sending invoices, the following diagram was created: I wanted to utilize a message-based approach, based upon my past experiences and satisfaction with the pattern. While one could argue that the current processing volume does not warrant a message-based approach, I did want to prove out this concept for use when the system gains more popularity. About CloudAMQP is an open-source message broker solution, which allows asynchronous communication between different components of a multi-tier application. is RabbitMQ wrapped into a service offering. Heroku not only offers CloudAMQP at price levels that match working directly with CloudAMQP, but also makes CloudAMQP very easy to install into an application. My experience with RabbitMQ and the ease of getting the message broker up and running made this product solution quite easy for me. RabbitMQ CloudAMQP In a message broker solution, there are three main elements: that has a message to be processed Producer that is the mechanism to store messages until they are processed Queue that watches the queue and handles the necessary processing Consumer The producer requesting a task to be completed places a message on what is called a queue. In this case, the Invoice Submission process is handed off to an asynchronous process - which avoids making the Trainer wait for the invoicing process to complete. As a result, once the Trainer submits the request, the Fitness application can navigate to other sections of the application and continue working. On the services side, the CloudAMQP service now has a message to be processed. A consumer (running in the Fitness application service within Heroku) listens for any messages to arrive and pulls them off the queue for processing, In the case of the Fitness application, the messages will be processed and sent to the customers via the Twilio service. Invoicing Example At a high level, the following actions take place during invoice processing: Trainer navigates to the Create Invoices menu option to request a list of current invoices. The Spring Boot service performs the necessary actions to return a List<InvoiceDto> of invoice data. The trainer can make adjustments to the invoice list, including changing a session to no-cost. When ready, the trainer uses the button to initiate the invoice process. Submit Invoices The Spring Boot service persists the InvoiceDTO items. Each InvoiceDTO is sent to CloudAMQP and added to the queue for processing asynchronously. An instance of the Spring Boot service picks up the job from the queue, processes the request, which sends an SMS message to the client for the invoice. At this point, each InvoiceDto is available for the trainer to view. The Customer Flow The customer receives an SMS message similar to what is displayed below: At that point, the end-user can use the first link to view an online version of their invoice: Using the invoice, the customer can click the button to pay for their sessions using Venmo or they can click the second link on the original SMS message. Both messages navigate the user to the following page: Pay Invoice using Venmo Here the user can sign-in or sign-up for Venmo. Once authenticated, the user can accept the request for payment and pay using the Venmo mobile application. Configuring CloudAMQP in Heroku with Spring Boot Getting started with CloudAMQP in is as easy as running the following command from the Heroku CLI: Heroku heroku addons:create cloudamqp:lemur Heroku adds everything your app needs to start working with the queue. Now we need to configure our app to use the queue, and define the message that we'll be passing through the queue. The properties related to CloudAMQP will now be available in the Heroku instance: These configuration values are ultimately set in Heroku as shown below: Within Spring Boot, the pom.xml needs to be updated to include the following dependency to support Rabbit AMQP: <dependency> <artifactId>spring-boot-starter-amqp< dependency> org.springframework.boot < > groupId </ > groupId /artifactId> </ Within the application.yml the following properties are introduced in order to allow the values to be dynamic: jvc: messaging: invoice-topic-exchange: invoice-topic-exchange invoice-processing-queue: invoice-processing-queue invoice-routing-key: invoice-processing.# The three custom properties are available in Spring Boot as part of the MessagingConfigurationProperties bean: @Data @Configuration( ) @ConfigurationProperties( ) public { private invoiceTopicExchange; private invoiceProcessingQueue; private invoiceRoutingKey; } "messagingConfigurationProperties" "jvc.messaging" class MessagingConfigurationProperties String String String Finally, the messaging configuration class is configured as shown below: @RequiredArgsConstructor @Configuration @EnableRabbit public { private final int MAX_CONSUMERS = ; private final ConnectionFactory connectionFactory; private final MessagingConfigurationProperties messagingConfigurationProperties; @Bean public AmqpAdmin amqpAdmin() { RabbitAdmin(connectionFactory); } @Bean(name = ) SimpleRabbitListenerContainerFactory invoiceProcessingRabbitListenerContainerFactory() { SimpleRabbitListenerContainerFactory factory = SimpleRabbitListenerContainerFactory(); factory.setConnectionFactory(connectionFactory); factory.setMaxConcurrentConsumers(MAX_CONSUMERS); factory; } @Bean(name = ) Queue invoiceProcessingQueue() { Queue(messagingConfigurationProperties.getInvoiceProcessingQueue(), ); } @Bean(name = ) TopicExchange invoiceTopicExchange() { TopicExchange(messagingConfigurationProperties.getInvoiceTopicExchange()); } @Bean Binding invoiceBinding(@Qualifier( ) Queue queue, @Qualifier( ) TopicExchange exchange) { BindingBuilder.bind(queue).to(exchange) .with(messagingConfigurationProperties.getInvoiceRoutingKey()); } } class MessagingConfig static 2 return new "invoiceProcessingRabbitListenerContainerFactory" new return "invoiceProcessingQueue" return new false "invoiceTopicExchange" return new "invoiceProcessingQueue" "invoiceTopicExchange" return At this point, Spring Boot can now connect to CloudAMQP to process messages in the topic exchange and queue. Processing Requests To process invoices using the CloudAMQP service, a request enters the system via the InvoiceService (the ): producer private sendMessages(List<Invoice> invoices, boolean resend) throws FitnessException { (CollectionUtils.isNotEmpty(invoices)) { (Invoice invoice : invoices) { handleInvoice(invoice); invoicePublisher.sendMessage( InvoiceProcessingRequest(invoice.getId(), userData.getTenant().getTenantProperties().getTimeZone(), userData.getTenant().getTenantProperties().getVenmoId(), resend)); } } } void if for new The InvoicePublisher class performs the task of sending the message to CloudAMQP (the ): queue @TransactionalEventListener(fallbackExecution = ) public sendMessage(InvoiceProcessingRequest invoiceProcessingRequest) throws FitnessException { { (invoiceProcessingRequest != ) { rabbitTemplate.convertAndSend(messagingConfigurationProperties.getInvoiceTopicExchange(), messagingConfigurationProperties.getInvoiceRoutingKey(), objectMapper.writeValueAsString(invoiceProcessingRequest)); } } (JsonProcessingException| AmqpException e) { FitnessException(FitnessException.UNKNOWN_ERROR, + e.getMessage()); } } true void try if null catch throw new "Error attempting to process invoice: " From there the InvoiceProcessor class (the running on a Spring Boot service API instance), receives the message and processes the request: consumer, @RabbitListener(containerFactory= , queues= ) @Transactional public receiveMessage( message) { { InvoiceProcessingRequest invoiceProcessingRequest = objectMapper.readValue(message, InvoiceProcessingRequest.class); Optional<Invoice> optional = invoiceRepository.findById(invoiceProcessingRequest.getInvoiceId()); (optional.isPresent()) { smsService.sendSmsInvoice(optional.get(), invoiceProcessingRequest.getTimeZone(), invoiceProcessingRequest.getVenmoId(), invoiceProcessingRequest.isResend()); invoiceRepository.save(optional.get()); } { log.error( , invoiceProcessingRequest.getInvoiceId()); } } (Exception e) { log.error( , message, e); } } "invoiceProcessingRabbitListenerContainerFactory" "#{messagingConfigurationProperties.invoiceProcessingQueue}" void String try if else "Could not locate invoiceId={}" catch "An error occurred attempting to process message={}" The SmsService sends the invoice information over SMS and the Invoice object is marked as processed. Conclusion Before this functionality was introduced, my sister-in-law was manually sending invoices to her clients. She would look back at the sessions that had been completed since their last invoice, then note the cost for each session and the date range. The next step would be to paste a generic message about the invoice and update the values to match her client's information. The new process takes a mere fraction of the time to complete and has the ability to resend invoices at a later time. The system is smart enough to know which clients to invoice and which invoices still remain open. This allows my sister-in-law to focus on training her clients knowing that all of the invoicing goals (noted above) have been met. In a similar fashion, Heroku provides the necessary tooling to introduce messaging concepts into an existing project with little effort. Just like the Heroku service does the DevOps work for me, CloudAMQP provides a quick setup and allows me to turn my attention to building intellectual property within the Fitness application to yield a better application experience. In the current state, my Heroku ecosystem for the Fitness application currently appears as shown below: I look forward to building upon this design as development continues across the feature roadmap. Have a really great day! Also published at https://dzone.com/articles/leveraging-cloudamqp-within-my-heroku-based-saas-s