Internal versus external events

Inevitably, for those building event-driven architectures (or even message-based architectures), the need arises to publish events to some outside consumer. This consumer could be another solution built by the same team, an adjacent team, or consumers outside the firewall boundary.

Since these systems often already use messaging and events internally, the question is posed: Are the events we publish internally the same as publishing externally?

The answer is no.

External message contracts need to have their own life cycles. They have different constraints, different rates of change, and different abilities to from clients consume changes.

Internally, since we own the place, we can make changes to messages at typically much higher rates than for external clients. External clients have their own priorities, teams, goals and so on that we can’t just rock the boat because it feels right. Migrating internal consumers might be a cinch for new messages. But external ones, we need to think about their needs.

Instead, we need to build gateways as to prove an anti-corruption layer (from the Domain-Driven Design terminology) to broker our internal events to external ones:


The messages flowing from internal publisher to the gateways may even look exactly the same, but we can’t treat them as being the same. In order to preserve our own internal autonomy and shield ourselves from the constraints of the outside world, we need to maintain separation between our internal and external messages.

External message design

The nice thing about this approach is that we can allow our external events to grow on their own. External events often need a bit of “translation” from internal events, as the ubiquitous language internal to a bounded context is not the same ubiquitous language used between bounded contexts. A “customer” to billing is not the same as a “customer” to support, for example.

Even the stimuli for publishing external events can differ. We might use push mechanisms, pull mechanisms, events, or polling to determine how, when and why external events get published. We might need to de-duplicate events published, to not force the idempotency issue on our clients.

We might design the shape of these events to include aggregate information that wouldn’t exist internally. External clients may be too burdened to call back for additional details, so these external events may be “fatter” than our internal ones.

In short, our external events often have reasons for change and requirements for design completely orthogonal than our internal events. To mitigate this problem, use anti-corruption layers along with gateways to shield both you and your consumers from the natural evolution of your internal system.

About Jimmy Bogard

I'm a technical architect with Headspring in Austin, TX. I focus on DDD, distributed systems, and any other acronym-centric design/architecture/methodology. I created AutoMapper and am a co-author of the ASP.NET MVC in Action books.
This entry was posted in DomainDrivenDesign, SOA. Bookmark the permalink. Follow any comments here with the RSS feed for this post.
  • Absolutely. Even if the internal and external messages are identical, they won’t remain that way for long. Using a canonical message pattern saves a lot of headaches. It’s also important to remember that “external” means “not deployed concurrently with your app” rather than “outside your organization”. In a corporate environment, an “external client” developer could be in the next cube over.

  • This may sound as a very noob question (which it probably is) – what resources can you recommend to learn event-driven architecture from?

  • Pingback: Scott Banwart's Blog › Distributed Weekly 193()

  • Daniel Marbach

    I think this is a good blogpost. But the advice to always have an anti corruption layer is pretty critical. I think you have to consider always the additional effort vs. which partners are involved. I’ve seen companies taking the route with time lines. You get notified when V2 of an event is introduced alongside with a migration document. You get a timeline (let’s say 6 months) to migrate. After 6 months the V1 is turned off.

  • Riccardo Di Nuzzo

    This post appears to be quite old but I’d like to point out something.
    Inside the External Message Design paragraph you said that “…External clients may be too burdened to call back for additional details, so these external events may be “fatter” than our internal ones…”
    But this is not following other documentation sources like the following where the writer is saying that External message should contains only ids

    Can you clarify this point please?