0

I'm working on a project using springboot, spring cloud netflix, etc., to build microservices.

And for some of the async communication, I'm using Spring Cloud Stream to produce and consume events. e.g. After a business contract is drafted in the contract-service, the service publish a contract-created-event, which will be consume by auditing-service, to init a auditing process. And also, user-service will consume the event to create notifications for related parties.

The scenario is I have many events, consumer will subscribe the interested events based on event types. The problem I have is, I have many event-types, soon, my configuration file is flooded with the channel configurations. e.g.

spring.cloud.stream.bindings.creation.destination=contract-creation
spring.cloud.stream.bindings.revocation.destination=contract-revocation
spring.cloud.stream.bindings.termination.destination=contract-termination
...

I'm doing something wrong? I'm considering those alternatives:

  1. David Turanski! has a example which will selectively consume the event by type. Which is a good solution, but I'm thinking, Is Spring Cloud Stream have the native solution?
  2. Should I use Apache Camel or Spring Integration? I don't have complicated routing rules, these frameworks seem like an overkill.

I'm quite a newbie in messaging, hoping folks here could point me to a right direction.

frankskywalker
  • 85
  • 1
  • 11

2 Answers2

3

It really depends, and I'm terrible sorry for starting an answer with that.

Having a single channel with selectors is the simplest choice, but with the caveat that every single consumer will consume all messages from that destination. If this is your use case, then go for it.

Another use case, would be an Event Sourcing type, where most of the consumers are only interested in a subset of events, and you perhaps would be better by placing Events (or better, aggregate roots) on each destination. This would allow you to scale better, and avoid unnecessary chattiness from the broker to consumers.

In your example you could have something like this instead:

public interface Contracts {

    @Output("contract-creation") MessageChannel creation();
    @Output("contract-revogation") MessageChannel revogation();
    @Output("contract-termination") MessageChannel termination();

} 

That would create one Topic for each eventType, and perhaps is a bit overkill

Perhaps you should create an interface Event with a Type and have your Events descend from it, and then have this instead:

public interface Events {
    @Output MessageChannel user();
    @Output MessageChannel contract();
}

Now, all Contract events (creation,revogation,termination) would go to the same Destination. And on the receiving end you can create selectors to choose which one to apply:

@StreamListener(target = "contract", condition = "payload.type=='created'")
    public void contractCreated(@Payload ContractCreatedEvent){

    }

    @StreamListener(target = "contract", condition = "payload.type=='terminated'")
    public void contractTerminated(@Payload ContractTerminatedEvent){

    }
Vinicius Carvalho
  • 3,994
  • 4
  • 23
  • 29
  • Would it be better to put the type info in the message header, and use @StreamListener(target = ConsumerChannels.CONSUMER, condition = "headers['type']=='contract-created'") ? That way if I don't need the 'type' field for the entity, I could get a cleaner entity. – frankskywalker Jul 19 '17 at 11:54
  • using headers is another choice, but I would not say it's better, as it depends on the domain design. If you are modeling events whose types are important for storage and retrieval, then it's not pollution to have a property called type in a base class. – Vinicius Carvalho Jul 19 '17 at 15:18
  • Hi, Vinicius When I use your code condition="payload.type=='terminated'", and put the type field in payload, I got Cannot find a @StreamListener matching for message with id: xxxxx. But using headers['type'], and put type in header works fine. So, is it possible to use payload in condition spel? – frankskywalker Jul 25 '17 at 14:05
  • This article can help with implementation of mentioned approach https://dturanski.wordpress.com/2017/03/26/spring-cloud-stream-for-event-driven-architectures/ – Tioma Dec 27 '17 at 10:50
1

The condition expression property mentioned in David's blog post is baked into the @StreamListener annotation, so you can route different event types from the same destination to different listeners.

Gary Russell
  • 166,535
  • 14
  • 146
  • 179