Eventuous / eventuous

Event Sourcing library for .NET
https://eventuous.dev
Apache License 2.0
447 stars 71 forks source link

How would you "share" projections between bounded contexts? #5

Closed paulvanbladel closed 3 years ago

paulvanbladel commented 3 years ago

Alex, Congrats with this new repo, nice continuation on your book. Thanks a lot. Not sure if it's appropriate to ask a question here. If so, simply disregard my question. I think that in https://eventuous.dev/docs/prologue/the-right-way/, you make a very good point about the orthogonality of message brokers and event sourcing. My questions is about projections and the most elegant way to "share" a projections with other bounded contexts. So, let's stick to the canonical case of a customer service which manages (single source of truth write access) a customer status. The customer services is event sourced and needs also a customer-status projection for his own purpose. Other services services needs also the customer-status projection, but also when customer service is offline. How would you tackle this? Cheers paul.

josephcummings commented 3 years ago

@paulvanbladel as it sounds your canonical example and use-case follows Domain Driven Design, you could leverage an Anti Corruption Layer in other Bounded Contexts that have a dependency on information from the Customer Bounded Context.

As an example of an Anti Corruption Layer in this use-case, it may be a simple, lightweight service that consumes events from EventStoreDB and projects them into a database of the Bounded Context with a dependency on information from the Customer Bounded Context.

This decreases hard dependency on the Customer Bounded Context and gives the implementing Bounded Context complete control over the information it depends on from other Bounded Contexts and how it translates it to fit the implementing Context.

Slightly off topic but if you are using the Microservice Architecture, this harmonises well with 'database per microservice' and various other principles that underpin how the Microservice Architecture is generally implemented.

paulvanbladel commented 3 years ago

Thanks Joseph, Sure, each microservice has own dB. OK, so we would not rely on integration events, but subscribe to the event store of the customer service for that particular event. That make sense for two reasons:

josephcummings commented 3 years ago

Exactly that, as you thin out dependency on the Customer Bounded Context to only depend on the EventStoreDB that serves it and the domain events with-in, if the Customer Bounded Context's database goes offline or is down for maintenance, it won't affect other Bounded Contexts and Microservices as they have built up their own projections, in their own databases.

paulvanbladel commented 3 years ago

So, for the event store event delivery, what are the distributed computing assumptions we can make. I mean in case of integration events we need to care about idempotence and out-of-order delivery. Is this different we consuming from an event store?

josephcummings commented 3 years ago

Considerations and concerns around concurrency issues would be the same whether you are consuming Integration and/or Domain Events and whether it's EventStoreDB or a Message Broker.

Sticking to the above use-case, for simply consuming Domain or Integration events from another Bounded Context and projecting a (Read)Model: I would use a single consumer executing on a single thread, consuming from a Persistent Subscription where you can guarantee you will receive the events in the order of which they were applied to a stream in EventStoreDB.

Of course this is not scalable; EventStoreDB has different Consumer Strategies to help with this such as the Pinned Consumer Strategy to ensure work is balanced (and sticky) by event(s) stream identifier - however as documented, this is no guarantee and usual measures for dealing with out-of-order events and concurrency issues will need to be implemented, see the disclaimer here.

Just as in the world of message brokers, processing events in a group of consumers running in parallel processes will most likely get events out of order within a certain window. For example, if a consumer group has ten consumers, ten messages will be distributed among the available consumers, based on the strategy of the group. Even though some strategies make an attempt to consistently deliver ordered events to a single consumer, it's done on the best effort basis and there is no guarantee of events coming in order with any strategy.
alexeyzimarev commented 2 years ago

My five cents here would be to avoid direct coupling to domain events from another context. It is a good approach, in general, to build an ACL as a small read model, totally legit. Just remember that the ACL code is then coupled to the language of the context where it consumes the events from. We could argue that's exactly what ACLs are for, so in many cases it's ok. In other cases, you might consider making a BC that shares its data with others to become an Open Host. Then, it would need to publish events in stable contracts. So, you'd use a Shovel to establish the conversion.

In many cases, you'd need some sort of reporting store, which consolidates data from several contexts as a read model. Then, it becomes a context on its own. Keep in mind that such a reporting context will always be a Conformist to all other contexts where it gets the events from. But I don't think it can be avoided, so we just have to live with that.