-1

I am learning stuff and have made 2 microservices, Listener and MyApp. Listener is a jms listener (consumer of messages) and MyApp is the app sending messages to queue.

I have 2 type of events CREATE and UPDATE and I want to maintain a single queue. So, my initial solution looked like below, here the Event class represent the objects to be sent to queue.

public class Event{ private String eventType; private DomainObjectA a; private DomainObjectB b; private DomainObjectC c; private DomainObjectD d; } 

The initial solution was as shown below and it worked:

if("CREATE".equals(eventType) build DomainObjectA; build DomainObjectB; build DomainObjectC; build DomainObjectD; set above objects in Event object send Event object to queue else if("UPDATE".equals(eventType) build DomainObjectC; set above objects in Event object send Event object to queue 

Then I thought to refactor my code in a way that depending on the event type,the MyApp will be forced to build the correct objects for type saftey. I want to prescribe my client to build the appropriate objects rather then let the client make a decision. So, I came up with below thought:

 public class Event{ private String eventType; //marker interface which will be implemented by the 2 type //of payload objects (create and update), so that I can //program to interface and same idea can be extended to future // types of event payloads private EventPayload payload; } public interface EventPayload{} public class CreateEvent implements EventPayload { private DomainObjectA a; private DomainObjectB b; private DomainObjectC c; private DomainObjectD d; } public class UpdateEvent implements EventPayload { private DomainObjectC c; } 

Now depending on the type of event(create or update) in my MyApp project, I want to use the corresponding EventPayload type to set data i.e. if event type is create i want to build CreateEvent object while when event type is update i want to build UpdateEvent object.

public enum EventEnum { CREATE("CREATE", new CreateEvent()), UPDATE("UPDATE", new UpdateEvent()); private final String type; private final EventPayload payload; //interface for the event types private EventEnum(String type, EventPayload payload) { this.type = type; this.payload = payload; } public String getType() { return type; } public EventPayload getPayload() { return payload; } } 

So for create event in my MyApp project, I want something like

 //trying to program to interface here so that I can handle diff type of payloads for same property EventPayload payload = EventEnum.CREATE.getPayload(); 

For update event in my MyApp project, I want something like

EventPayload payload = EventEnum.UPDATE.getPayload(); 

In my Listener project I want to check the type of event and based on whether its CREATE or UPDATE I want to do something like below:

 @JmsListener(destination = "event-queue", containerFactory = "myFactory") public void receiveMessage(AlertEvent alertEvent) { recordStoreService.save(alertEvent); } public void save(AlertEvent alertEvent){ String event = alertEvent.getType(); EventPayload payload = alertEvent.getPayload(); if(null == event || null == payload){ LOGGER.error("Either Event or Payload is null"); return; } if("CREATE".equals(EventEnum.CREATE)) insert(payload); else update(payload); } private void insert(CreateEvent payload){ objetAMapper.insert(payload.getDomainObjectA()); objetBMapper.insert(payload.getDomainObjectB()); objetCMapper.insert(payload.getDomainObjectC()); objetDMapper.insert(payload.getDomainObjectD()); } private void update(UpdateEvent payload){ objetCMapper.insert(payload.getDomainObjectC()); } 

I have 2 questions:

Is my design i.e. using enum in this way a naive idea and not a good one. If not what will be a good way to handle things in this scenario.

Programming to interfcae idea backfired as I realized I am not using it in the right way. Please can you guide me as to where am I making mistake.

Please go easy on me as I am learning things and just beginning to think about proper design. Thank you.

2
  • 1
    Note that enum constants are singletons, which means that there is only a single instance of each constant (CREATE and UPDATE). This means you cannot have different UPDATE instances with different payloads, and that's most likely not what you want. payload should not be a member variable of the enum.
    – Jesper
    CommentedJun 10, 2021 at 8:17
  • @Jesper thank you !!CommentedJun 13, 2021 at 13:10

2 Answers 2

0

I have no idea about how JMS works, so I won't be able to provide you with an exact solution, but I have some experience working with RabbitMQ.

As far as I can tell you have a save() method. It should not check for the type of event it receives, but rather receive the payload and produce the event with the correct type.

@JmsListener(destination = "event-queue", containerFactory = "myFactory") public void receiveMessage(AlertEvent alertEvent) { if(EventType.CREATE.equals(alertEvent.type) recordStoreService.save((CreateEvent) alertEvent); else recordStoreService.update((UpdateEvent) alertEvent); } 
class MessageProducer { ... void save(CreateAlert payload) { AlertEvent event = new CreateEvent(payload); queue.send(event) } void update(UpdateAlert payload) { AlertEvent event = new UpdateEvent(payload); queue.send(event) } public static class CreateEvent extends AlertEvent { private final String type = EventType.CREATE private EventPayload payload; ... } public static class UpdateEvent extends AlertEvent { private final String type = EventType.CREATE private EventPayload payload; ... } } 

I hope I understood correctly, that the save method produces messages, which get consumed and saved.

    0

    If I go it, on the producer-side there's

    //At least 2 event types public enum EventType {CREATE, UPDATE} //Definition simplified on purpose //Only one concrete Event class (data structure) public class Event { private EventPayload payload; private EventType type; public Event(EventType type, EventPayload payload){ ...} ... } //Many possible and different payloads public interface EventPayload { ... } 

    To me, Event is just an envelop. The abstraction is on the payload which can be different. Unless you need to work with payloads (what I suspect you don't), the only you need is to know how to serialize and deserialize them.

    I have made a test with JacksonMapper and I have realised that EventPayload doesn't even need a single getter. Basically, the interface acts like a marker interface. If your serializer is sophisticated enough, it will inspect the concrete payload instance and look for public methods. Usually getters. Then, what it takes is to put the serialized Event within a javax.JMS.TextMessage

    The last thing that left on the producer-side, is the producer itself. It can be anything. As for example an EventFactory, which receives an EventType and maybe something else.

    On the consumer side, I see at least two ways to handle the different data structures that might arrive.

    • If there are only 2 event types and you don't expect to have many more. You could create one queue for each event type. On the consumer side, you could set different deserializers|mappers to each queue. To be honest, this's the solution that scales worse, but managing different queues for different kinds of "messages" wouldn't be that rare. We usually do it to identify/segregate origins or message sources. In other words, one queue for producers. The goal is to make easy message handling.

    • The second would be fetching the message from the only queue, look for matching event types and run the proper deserializer|mapper based on the matching.

    In both cases, the handling method changes

    @JmsListener(destination = "event-queue", containerFactory = "myFactory") public void receiveMessage(TextMessage message) { //Call your mapper here upon message.getText(); //e.g inputMessageHandler.handle(mapper.toObject(message.getText())); //leave what to do with the Object to the handler, not to the JMS queue //listener. This is last should be a mere dispatcher } 

    One thing I like about turning the payload into the abstraction instead of the whole Event is that I can evolve Event as an envelope. I could add more attributes to help the consumer side to handle the incoming message. For example, I could add (message) version , procuerId, producedAt, correlationId (an id to correlate events, e.g within a transaction or request), target (for example, an URL or queue for feedback)

      Start asking to get answers

      Find the answer to your question by asking.

      Ask question

      Explore related questions

      See similar questions with these tags.