9

I'm doing an research-project where I'm researching the options to handle changes in an event-driven microservice architecture.

So, let's say we got an application where we got four different services. Each of these services has an own database to store local data.

In this setup, the four services communicate with each other using an Event Bus. So when something happens in a service, it publishes an event. All the other services that are interested in that event will process it in their own way.

In that case the different services in the architecture need to have "contracts" about the content of these events (attributes etc.). So services have a "loosely coupled dependencies" to these events

My question is: How can we handle changes in these events?

So, let's say service A registers new users in the application. So it sends an " "UserRegistered" event. Service B picks up that event and processes it. But some developer on the team of service C decided that they also need a gender of a registered user. So the event is changed and the attribute gender is added to the "UserRegistered" event.

How can we make sure that Service B can still pickup the same event with that extra attribute without redeploying?

And are there other ways to approach this problem then versioning these events?

  • What format are your messages, or is that something that you can design? Some message formats allow for optional attributes. Depending on the implementation of the reader, you can add optional attributes without needing to update all of the readers. – Thomas Owens Sep 21 '16 at 13:47
  • I'm free in choosing a format to use for my messages. I think using JSON is the best way to go. It's important that these different services are built in different languages. Thats why a general format like XML or JSON is necessary. – CGeense Sep 21 '16 at 13:51
1

Events aren't about what changed. They're about when something changed.

I can create an event system completely decoupled from the contents that changed. That way all I learn from an event is that an object has been updated. If I even care that the object has been updated I'll then tell whatever knows how to talk to that object to go ask it what changed.

That doesn't solve the problem of communicating these changes. It just stops it from becoming part of the event system.

An example of one way to solve the problem of differing versions of data is to have the observer create and hand the observed object a collection. The observed object populates the collection with it's latest data and when control returns you (the observer) have what you need. If there is extra that you don't care about, because you never heard of it, you simply ignore it.

Many other ways to skin that cat but that's one I've made work in exactly this case.

  • Wouldn't this dramatically increase traffic between services? Using the example in the question, a UserRegistered event, if there was an event that didn't contain information about the user, there would be 1 published message to the bus and then {number of interested services} requests to the user service or published messages to the bus. Then, there would be {number of interested services} messages back of various sizes. Although I do think that this is probably a cleaner design on paper, if the performance is an concern, it breaks down in any non-trivial system, especially over a network. – Thomas Owens Sep 21 '16 at 13:58
  • @ThomasOwens Sending data with the event means if I have N observers I send N messages. Sending the event alone means I send 3N messages only 1 of which has the data packet. That scales just fine even over a network. The only significant downside is it triples your lag. Not saying you can't find a more optimal solution for a particular situation. I'm demonstrating that event systems and data versions don't have to be coupled. – candied_orange Sep 21 '16 at 14:06
  • 1
    The whole idea of this event bus is to decouple the different services. By using a piece of middleware, we can make sure that these services dont know each other and can exist and communicatie without knowing of each other's existence. If we remove state from these events and let services connect to each other directly, we are coupling these services. In that way, we can never redeploy a single service without having to redeploy the rest – CGeense Sep 21 '16 at 14:10
  • 1
    The point here is that event system or not you need extensible data. JSON or XML do that fine if you don't go changing the names or structures that have been previously established. I've done the same with collections. The event system shouldn't care about genders. If it's sending data it should just pass them along and something on the other end will either care about genders or it won't. – candied_orange Sep 21 '16 at 14:41
1

Frameworks like NServiceBus handle this by using event versioning with polymorphic message dispatch.

For example, version 1 of Service A might publish an event as IUserRegistered_v1. When Service A version 1.1 needs to include an additional field, it might declare interface IUserRegistered_v1_1, which would inherit from IUserRegistered_v1 as well as declare some additional fields.

When Service A publishes an IUserRegistered_v1_1 event, NServiceBus will dispatch the message to all endpoints that handle either IUserRegistered_v1 or IUserRegistered_v1_1.

0

Incremental Improvement

A simple change to the model is that when listeners register as an observer, they include a list or other structure of the data elements they wish to know about. This can work if the data returned from the service is simple but if you have a fair amount of hierarchical data, this can get really complicated to implement.

Rock-solid

If you really want a robust way to do this design the service such that it keeps a history of changes that have been made to the data it stores. Essentially, you never update records in your database, you add new records where each one represents the change. Each of these new records is associated to an event id which identifies the action. An even record is stored with all the relevant info about the change (who, what, when, etc.) This has some other benefits that are outside of the scope of this answer but are discussed in this article about the CAP theorem.

When a change is made, you create the event record and add all the new data to your database. Then you publish an event to the listeners that contains (minimally) the event id. The listeners then can request the data associated with that id and get the version of the data associated with it. Each listener then is able to get whatever it needs without coupling the needs other different listeners together. I would advise that you add a subset of the most commonly used data fields to the event message so that listeners may filter out events that they aren't interested in. This can reduce the chattiness of the process and some listeners might never need to call back at all. This also protects you from timing issues. If you just call back to the service and get the data based on key, there might be other changes that have happened in between getting the event and retrieving the data for it. This might not matter for all listeners but it can create big issues if you need to know about all changes. The incremental design improvement above is compatible with this approach if you really want to turn it to 11.

Some of this may be overkill for what you need to do but in my experience if you don't have a precise way to look at how a record is changing over time, you or someone working with your data will eventually want it.

-1

@CandiedOrange makes a valid point in a comment to his own answer regarding extensible data formats like xml.

It shouldn't matter as long as you're adding data. Do provide sensible defaults for older events / non-required fields, however.

You should only have to update the services that care about - in this case - Gender. An xml / json parser should be able to ignore extra data for the other services. This depends on your choice of parser and event data format, of course.

I do not agree with events not having the relevant data though. For event sourcing, events should define what has changed. On receiving an event, other services shouldn't have to retrieve data from the source of the event.

  • My point is that it needs to handle all kinds of changes. Think of a service that broadcasts an event. And that event contains a property that is obsolete and should be removed. Even if these other services are not using the property, it will break them simply because they are expecting it. So i read this article on martinfowler.com about consumer driven contracts: martinfowler.com/articles/consumerDrivenContracts.html When applying this principle. Each provider(event) knows what is expected of him. with that infomation he can validate if he breaks any comsumers. – CGeense Sep 26 '16 at 7:13

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.