Mark a small subset of events as public, keep the rest private by default.
Domain Events are not only useful for communicating to other Bounded Contexts, but also for organising and decoupling code inside the Bounded Context. In CQRS/Eventsourcing architectures, this is even the primary way of organising the code. It’s very convenient to publish all these events using a single bus, effectively making them all public. This is a problem when some of these events contain sensitive data.
More importantly, the outside API is tightly coupled to the internal structure of the Bounded Context. Changing the internals would force an API change.
Set up separate messaging channels for inside the Bounded Context and outside. Keep all events private by default, and indicate the ones you want to make public with an explicit
@Public annotation, a marker interface, or an
isPublic():bool method. When emitting events, the event publishing mechanism knows to read the annotation and either send the event on the private channel only, or on both the private and the public channel.
In general, my feeling is that the problem seldom occurs when the Domain Events have been chosen carefully to reflect the business domain, using Ubiquitous Language, and at the right granularity. These events then tend to become stable very quickly during development, and rarely need to be altered. When there are significant changes in the domain, the events may need to change, but in these cases you’ll need an API change anyway.
That said, it’s a universally useful heuristic in software design to keep everything as closed off as possible, and only open up things where there’s a good case for it.