Open In App

Event-driven Applications With Spring Cloud Stream

Last Updated : 26 Feb, 2024
Improve
Improve
Like Article
Like
Save
Share
Report

In Spring Boot, Event-driven architecture is a paradigm, and it can be software component communication through the production, detection, and consumption of events. Spring Cloud Stream is a popular framework built on top of Spring Boot that can be used to simplify the development of event-driven microservices and provide the abstractions and tools to build, deploy, and scale event-driven applications effortlessly.

The Spring Cloud stream can abstract the complexities of the messaging middleware by providing a simplified programming model and introducing concepts like binder, binding, and channels.

Key Terminologies:

  • Binder: Binders can be used to connect the application to a messaging middleware like RabbitMQ.
  • Bindings: Bindings can be defined as the relationships between the input/output channels and destinations in the messaging system.
  • Channels: It can represent the input and output channels through the messages that flow in and out of your application.

Implementation of Event-driven applications with Spring Cloud Stream

We can develop a simple event-driven application using RabbitMQ messaging middleware. In this, we can create the course and the student publisher sends the message through the RabbitMQ broker and the receiver can receive the queue message.

Step 1: We can create the spring project using InteljIdea on creating the project add the below dependencies into your project.

Dependencies:

  • Spring Web
  • Spring Cloud Stream
  • Spring for RabbitMQ
  • Lombok
  • Spring Dev Tools

Once create the spring boot project your project file structure looks like the below image.

File Structure:

Project Structure

Step 2: Open the application.properties file and put the code below for the mongodb database and RabbitMQ server configuration.

application.properities file:

spring.data.mongodb.uri=mongodb://localhost:27017/EventDemo
server.port=8081
#RabbitMQ connection properties
spring.rabbitmq.host=localhost
spring.rabbitmq.port=5672
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest
spring.rabbitmq.virtual-host=/

Step 3: If you want to work with RabbitMQ message broker we need to download and install into your system.

RabbitMQ Download using this link

Step 4: Once download the rabbit open the server sbin path and using the below command to enable the RabbitMQ plugins into your system.

Command:

rabbitmq-plugins enable rabbitmq_management

Refer the below image for better understanding:

Enabling RabbitMQ plugins

Once enable the RabbitMQ plugins using below the command and run the server into your system.

service.bat start

Refer the below image:

Server running

Once start the server and its dashboard open in the port number 15672 and if ask the username and password both are gives as guest.
RabbitMQ Dashboard:

RabbitMQ Dashboard

Step 5: Create the new package and it named as model and in that package create the class named as Course

Go to src > java > com.gfg.eventdrivendemo > model > Course and put the below code:

Java




package com.gfg.eventdrivendemo.model;
  
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.data.annotation.Id;
  
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Course {
  
    @Id
    private String id;
    private String tutorialName;
    private String description;
  
}


Step 6: create the class in the same package and it named as Student.

Go to src > java > com.gfg.eventdrivendemo > model > Student and put the below code:

Java




package com.gfg.eventdrivendemo.model;
  
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;
  
@Document
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student {
  
    @Id
    String id;
    String studentName;
    String email;
    String branch;
}


Step 7: Create the new package and it named as repository and in that package create the interface named as CourseRepository

Go to src > java > com.gfg.eventdrivendemo > repository > CourseRepository and put the below code:

Java




package com.gfg.eventdrivendemo.repository;
  
import com.gfg.eventdrivendemo.model.Course;
import org.springframework.data.mongodb.repository.MongoRepository;
import org.springframework.stereotype.Repository;
  
  
@Repository
public interface CourseRepository extends MongoRepository<Course, String> {
}


Step 8: create the one more interface in the same package and it named as StudentRepository

Go to src > java > com.gfg.eventdrivendemo > repository > StudentRepository and put the below code:

Java




package com.gfg.eventdrivendemo.repository;
  
import com.gfg.eventdrivendemo.model.Student;
import org.springframework.data.mongodb.repository.MongoRepository;
import org.springframework.stereotype.Repository;
  
@Repository
public interface StudentRepository extends MongoRepository<Student,String> {
}


Step 9: Create the new package and it named as dto and in that package create the class named as CourseDTO

Go to src > java > com.gfg.eventdrivendemo > dto > CourseDTO and put the below code:

Java




package com.gfg.eventdrivendemo.dto;
  
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.data.annotation.Id;
  
@Data
@AllArgsConstructor
@NoArgsConstructor
public class CourseDTO {
  
    @Id
    String id;
    private String title;
    private String description;
}


Step 10: In the same package create the one more class named as StudentDTO

Go to src > java > com.gfg.eventdrivendemo > dto > StudentDTO and put the below code:

Java




package com.gfg.eventdrivendemo.dto;
  
  
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.data.annotation.Id;
  
@Data
@AllArgsConstructor
@NoArgsConstructor
public class StudentDTO {
  
    @Id
    String id;
    private String name;
    private String email;
}


Step 11: Create the new package and it named as service and in that package create the class named as CourseService

Go to src > java > com.gfg.eventdrivendemo > service > CourseService and put the below code:

Java




package com.gfg.eventdrivendemo.service;
  
import com.gfg.eventdrivendemo.dto.CourseDTO;
import com.gfg.eventdrivendemo.model.Course;
import com.gfg.eventdrivendemo.repository.CourseRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
  
import java.util.List;
import java.util.stream.Collectors;
  
@Service
public class CourseService {
    @Autowired
    private CourseRepository courseRepository;
  
    public List<CourseDTO> getAllCourses() {
        List<Course> courses = courseRepository.findAll();
        return courses.stream()
                .map(this::convertToDTO)
                .collect(Collectors.toList());
    }
  
    public CourseDTO createCourse(CourseDTO courseDTO) {
        Course course = convertToEntity(courseDTO);
        Course savedCourse = courseRepository.save(course);
        return convertToDTO(savedCourse);
    }
  
    private CourseDTO convertToDTO(Course course) {
        CourseDTO courseDTO = new CourseDTO();
        courseDTO.setId(course.getId());
        courseDTO.setTitle(course.getTutorialName());
        courseDTO.setDescription(course.getDescription());
        return courseDTO;
    }
  
    private Course convertToEntity(CourseDTO courseDTO) {
        Course course = new Course();
        course.setId(courseDTO.getId());
        course.setTutorialName(courseDTO.getTitle());
        course.setDescription(courseDTO.getDescription());
        return course;
    }
  
}


Step 12: In the same package create the more class named as StudentService

Go to src > java > com.gfg.eventdrivendemo > service > StudentService and put the below code:

Java




package com.gfg.eventdrivendemo.service;
  
import com.gfg.eventdrivendemo.dto.StudentDTO;
import com.gfg.eventdrivendemo.model.Student;
import com.gfg.eventdrivendemo.repository.StudentRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
  
import java.util.List;
import java.util.stream.Collectors;
  
@Service
public class StudentService {
  
    @Autowired
    private StudentRepository studentRepository;
  
    public List<StudentDTO> getAllStudents() {
        List<Student> students = studentRepository.findAll();
        return students.stream()
                .map(this::convertToDTO)
                .collect(Collectors.toList());
    }
  
    public StudentDTO createStudent(StudentDTO studentDTO) {
        Student student = convertToEntity(studentDTO);
        Student savedStudent = studentRepository.save(student);
        return convertToDTO(savedStudent);
    }
  
    private StudentDTO convertToDTO(Student student) {
        StudentDTO studentDTO = new StudentDTO();
        studentDTO.setId(student.getId());
        studentDTO.setName(student.getStudentName());
        studentDTO.setEmail(student.getEmail());
        return studentDTO;
    }
  
    private Student convertToEntity(StudentDTO studentDTO) {
        Student student = new Student();
        student.setId(studentDTO.getId());
        student.setStudentName(studentDTO.getName());
        student.setEmail(studentDTO.getEmail());
        return student;
    }
  
}


Step 13: Create the main package create the class named as MessageConfiguration

Go to src > java > com.gfg.eventdrivendemo > MessageConfiguration and put the below code:

Java




package com.gfg.eventdrivendemo;
  
import org.springframework.amqp.core.*;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.amqp.support.converter.MessageConverter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
  
@Configuration
public class MessageConfiguration {
  
    public static final String ROUTINGKEY = "greek_routing-key";
    public static final String QUEUE = "Greek for Greeks Queue";
    public static final String EXCHANGE = " Greek for greeks topic";
  
  
    @Bean
    public Queue queue() {
        return new Queue(QUEUE);
    }
  
    @Bean
    public TopicExchange topicExchange() {
        return new TopicExchange(EXCHANGE);
    }
  
    @Bean
    public Binding binding(Queue queue, TopicExchange topicExchange) {
        return BindingBuilder.bind(queue).to(topicExchange).with(ROUTINGKEY);
    }
    MessageConverter converter() {
        return new Jackson2JsonMessageConverter();
    }
  
    @Bean
    AmqpTemplate template(ConnectionFactory connectionFactory) {
        RabbitTemplate template = new RabbitTemplate(connectionFactory);
        template.setMessageConverter(converter());
        return template;
    }
}


Step 14: Create the main package create the class named as RabbitMQConfig

Go to src > java > com.gfg.eventdrivendemo > RabbitMQConfig and put the below code:

Java




package com.gfg.eventdrivendemo;
  
import org.springframework.amqp.support.converter.MessageConverter;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
  
@Configuration
public class RabbitMQConfig {
  
    @Autowired
    private MessageConverter customMessageConverter;
  
    @Bean
    public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory) {
        RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
        rabbitTemplate.setMessageConverter(customMessageConverter);
        return rabbitTemplate;
    }
}


Step 15: Create the new package and it named as MessageConvertor and create the class named as CustomMessageConvertor

Go to src > java > com.gfg.eventdrivendemo > MessageConvertor > CustomMessageConvertor and put the below code:

Java




package com.gfg.eventdrivendemo.messageConvertor;
  
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.gfg.eventdrivendemo.dto.CourseDTO;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageProperties;
import org.springframework.amqp.support.converter.MessageConverter;
import org.springframework.stereotype.Component;
  
@Component
public class CustomMessageConverter implements MessageConverter {
  
    private final ObjectMapper objectMapper;
  
    public CustomMessageConverter(ObjectMapper objectMapper) {
        this.objectMapper = objectMapper;
    }
  
    @Override
    public Message toMessage(Object object, MessageProperties messageProperties) throws org.springframework.messaging.converter.MessageConversionException {
        try {
            String jsonString = objectMapper.writeValueAsString(object);
            return new Message(jsonString.getBytes(), messageProperties);
        } catch (JsonProcessingException e) {
            throw new org.springframework.messaging.converter.MessageConversionException("Error converting object to JSON", e);
        }
    }
  
    @Override
    public Object fromMessage(Message message) throws org.springframework.messaging.converter.MessageConversionException {
        try {
            String jsonString = new String(message.getBody());
            return objectMapper.readValue(jsonString, CourseDTO.class);
        } catch (Exception e) {
            throw new org.springframework.messaging.converter.MessageConversionException("Error converting JSON to object", e);
        }
    }
}


Step 16: Create the new package and it named as MessageConvertor and create the class named as StudentMessageConvertor

Go to src > java > com.gfg.eventdrivendemo > MessageConvertor > StudentMessageConvertor and put the below code:

Java




package com.gfg.eventdrivendemo.messageConvertor;
  
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.gfg.eventdrivendemo.dto.StudentDTO;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageProperties;
import org.springframework.amqp.support.converter.MessageConverter;
import org.springframework.stereotype.Component;
  
@Component
public class StudentMessageConverter implements MessageConverter {
  
    private final ObjectMapper objectMapper;
  
    public StudentMessageConverter(ObjectMapper objectMapper) {
        this.objectMapper = objectMapper;
    }
  
    @Override
    public Message toMessage(Object object, MessageProperties messageProperties) throws org.springframework.messaging.converter.MessageConversionException {
        try {
            String jsonString = objectMapper.writeValueAsString(object);
            return new Message(jsonString.getBytes(), messageProperties);
        } catch (JsonProcessingException e) {
            throw new org.springframework.messaging.converter.MessageConversionException("Error converting object to JSON", e);
        }
    }
  
    @Override
    public Object fromMessage(Message message) throws org.springframework.messaging.converter.MessageConversionException {
        try {
            String jsonString = new String(message.getBody());
            return objectMapper.readValue(jsonString, StudentDTO.class);
        } catch (Exception e) {
            throw new org.springframework.messaging.converter.MessageConversionException("Error converting JSON to object", e);
        }
    }
}


Step 17: Create the new package and it named as publisher and create the class named as CoursePublisher

Go to src > java > com.gfg.eventdrivendemo > publisher > CoursePublisher and put the below code:

Java




package com.gfg.eventdrivendemo.publisher;
  
import com.gfg.eventdrivendemo.MessageConfiguration;
import com.gfg.eventdrivendemo.dto.CourseDTO;
import com.gfg.eventdrivendemo.dto.StudentDTO;
import com.gfg.eventdrivendemo.service.CourseService;
import com.gfg.eventdrivendemo.service.StudentService;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
  
import java.util.List;
  
@RestController
@RequestMapping("/students")
public class CoursePubisher {
  
    @Autowired
    private StudentService studentService;
  
    @Autowired
    RabbitTemplate rabbitTemplate;
  
    @Autowired
    MessageConfiguration messageConfiguration;
  
    @GetMapping
    public List<StudentDTO> getAllStudents() {
        return studentService.getAllStudents();
    }
  
    @PostMapping
    public ResponseEntity<StudentDTO> createStudent(@RequestBody StudentDTO studentDTO) {
        StudentDTO createdStudent = studentService.createStudent(studentDTO);
        rabbitTemplate.convertAndSend(messageConfiguration.EXCHANGE,messageConfiguration.ROUTINGKEY,createdStudent);
        return new ResponseEntity<>(createdStudent, HttpStatus.CREATED);
    }
  
}


Step 18: Create the new package and it named as publisher and create the class named as StudentPublisher

Go to src > java > com.gfg.eventdrivendemo > publisher > StudentPublisher and put the below code:

Java




package com.gfg.eventdrivendemo.publisher;
  
import com.gfg.eventdrivendemo.MessageConfiguration;
import com.gfg.eventdrivendemo.dto.CourseDTO;
import com.gfg.eventdrivendemo.service.CourseService;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
  
import java.util.List;
  
@RestController
@RequestMapping("/tutorial")
public class RegisterPublisher {
    @Autowired
    private RabbitTemplate rabbitTemplate;
  
    @Autowired
    MessageConfiguration messageConfiguration;
  
    @Autowired
    private CourseService courseService;
  
    @GetMapping
    public List<CourseDTO> getAllCourses() {
        return courseService.getAllCourses();
    }
  
    @PostMapping
    public ResponseEntity<CourseDTO> createCourse(@RequestBody CourseDTO courseDTO) {
        CourseDTO createdCourse = courseService.createCourse(courseDTO);
        rabbitTemplate.convertAndSend(messageConfiguration.EXCHANGE,messageConfiguration.ROUTINGKEY,createdCourse);
        return new ResponseEntity<>(createdCourse, HttpStatus.CREATED);
  
    }
  
  
}


Step 19: Open the main class and put the below code:

Java




package com.gfg.eventdrivendemo;
  
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
  
@SpringBootApplication
public class EventDrivenDemoApplication {
  
    public static void main(String[] args) {
        SpringApplication.run(EventDrivenDemoApplication.class, args);
    }
  
}


Step 20: Once created the project successfully run the application as spring project it can be start at 8081. Refer the below image for better understanding.

Rabbit Application Started

Course publisher:

Course publisher

Student Publisher:

Student Publisher

Message Queue at RabbitMQ Output:

Message Queue at RabbitMQ

Queue Message Output:

Queue Message

RabbitMQ Channel Output:

Rabbit Channel



Like Article
Suggest improvement
Previous
Next
Share your thoughts in the comments

Similar Reads