The Umbraco Notification Pipeline & Clean Domain Events: Architecting Decoupled Solutions

The Umbraco Notification Pipeline & Clean Domain Events: Architecting Decoupled Solutions

Umbraco

At some point every Umbraco developer hits the problem where you have business logic that needs to run when a page is published, when a product is published or when a form is submitted. The perfect place for that logic is in a notification handler. It works. It ships. And six months later, that handler has turned into a dumping ground for email sends, cache invalidations, search index updates, and three "quick" if-statements nobody wants to touch.

The notification pipeline itself isn't the problem. It's one of the most useful things Umbraco gives you - a clean, event-driven hook into the content lifecycle. The problem is what happens when business logic gets welded directly to it, instead of being kept a step removed.

This post walks through how to use Umbraco's notification system the way it's meant to be used, and how to layer clean domain events on top of it so your business logic doesn't live and die with your CMS internals.

What the Notification Pipeline Actually Gives You

Umbraco triggers notifications at nearly every significant step of the content lifecycle: save, publish, move, copy, delete, and more. Handlers can choose which notifications they want to subscribe to and execute code when they are triggered.

  • Most events are paired: a -ing event before it happens and a -ed event after it has occurred.
  • The "-ing" notifications are cancelable, meaning a handler can stop the operation before it happens
  • The "-ed" notifications are purely informational, the action already happened, and handlers just react to it
  • Handlers can be registered to run synchronously or asynchronously, depending on whether the work is quick or I/O-heavy
  • Everything gets wired up in one central place, so you always know where to look for what's listening

That last point is the real strength here: a single, predictable extension point instead of scattered event hooks across the codebase.

The Coupling Trap Most Teams Fall Into

The trouble starts when a handler stops being a notification handler and starts being a business logic engine.

  • The handler reaches directly into content properties and Umbraco-specific types to make decisions
  • Business rules end up buried inside infrastructure code, instead of living somewhere they can be found and reasoned about
  • Testing means spinning up Umbraco's content services just to verify a simple rule
  • The same logic can't be reused anywhere outside the CMS context, even when the business rule has nothing to do with Umbraco
  • One handler quietly grows five responsibilities, and nobody notices until it breaks

None of this is Umbraco's fault. It's what happens when a framework's extension point becomes the home for logic that should live somewhere else entirely.

Translating CMS Notifications into Domain Events

The fix is a thin translation layer. Instead of letting a notification handler make business decisions, its only job becomes converting a CMS-specific notification into a domain event your application actually understands.

  • A ContentPublishedNotification for a product page becomes something like ProductPageWentLive
  • The domain event carries plain, simple data - IDs, names, values - never Umbraco types like IPublishedContent or IContent
  • The notification handler's entire responsibility is translation: catch the notification, build the domain event, hand it off
  • No decisions, no side effects, no business rules inside the handler itself
  • If Umbraco changes how a notification is structured in a future version, only the translation layer needs to change, not your business logic

This one shift is what separates "code that happens to run inside a CMS" from "a real application that happens to use a CMS for content."

Where the Real Business Logic Should Live

Once a domain event exists, it needs somewhere to go. That's where a dispatcher sometimes called a mediator, comes in, sitting completely outside Umbraco-specific code.

  • The dispatcher receives the domain event and routes it to whichever handlers care about it
  • Each domain event handler does exactly one thing: send a notification email, update a search index, invalidate a cache, ping a Slack channel
  • Handlers can be tested in complete isolation, with no Umbraco context required at all
  • Adding a new reaction to an event means adding a new handler, not editing an existing one
  • Removing a feature means deleting a handler, not untangling it from three others

This is the payoff: business logic that's organized around what the business actually cares about, not around which Umbraco notification happened to trigger it.

Keeping It Async Without Breaking the Save

Content editors notice when saving or publishing suddenly feels slow. That's usually a sign that something I/O-heavy, an API call, a search reindex, an email send is sitting inside a synchronous handler and blocking the save pipeline.

  • Use asynchronous handlers for anything that involves network calls or external services
  • Push genuinely slow work, full reindexing, third-party syncs onto a background job instead of running it inline
  • Reserve cancelable notifications for cases where you truly need to stop an action, not as a place to run cleanup logic
  • Keep synchronous handlers fast and boring; if a handler is doing real work, it probably shouldn't be synchronous
  • Log failures at the boundary rather than letting a failed side effect silently swallow an editor's save

Best Practices

  • Keep notification handlers thin, they translate, they don't decide
  • Name domain events after what happened in the business, not what happened in Umbraco
  • Never let IPublishedContent or IContent leak into your domain or application layer
  • One handler, one responsibility resists the urge to combine reactions "since you're already in there"
  • Test domain event handlers on their own, with the dispatcher mocked out
  • Revisit old handlers periodically, they tend to accumulate responsibilities quietly over time

Conclusion

Umbraco's notification pipeline is genuinely well designed. The issue was never the tool, it's how easy it is to let business logic quietly move in and take up permanent residence inside it.

Separating CMS notifications from domain events isn't extra ceremony for its own sake. It's what maintains a decoupled codebase, enables you to reuse your business rules outside of the CMS, and prevents upgrades to future versions of Umbraco from turning into a safari hunt of hall-of-mirrors handlers. The translation layer is a modest investment that pays for itself every single time a new requirement land.

If you're maintaining an Umbraco solution where handlers have started to sprawl, this is usually one of the highest leverage refactors available and one that gets easier to justify the longer it's put off.

Written by
Nishant Vaghasiya

Nishant Vaghasiya

Technical Architect

I'm Nishant Vaghasiya, a Technical Architect and Umbraco & Sitecore Certified Developer at Arroact Technologies. I specialise in building digital solutions with Umbraco and Sitecore that are practical, scalable, and built to last.

Over the years, I've learned that the best solutions aren't always the most complex ones, they're the ones that make a team's day-to-day work simpler and give them confidence that the system won't let them down.

That's what drives me writing code that performs well, stays reliable, and continues to create real impact long after it goes live.

Related Blogsblue-line-vector-3

Enterprise Multi-Tenancy Architecture: Designing Scalable Multi-Site & Multi-Lingual Platforms
21 September 26 • 11 min read
Umbraco
Enterprise Multi-Tenancy Architecture: Designing Scalable Multi-Site & Multi-Lingual Platforms

Most enterprises don't run one website. They have portfolios, regional stores,…

Read More
Test-Driven Development (TDD) & Clean Architecture in Umbraco: Decoupling Domain Logic from CMS Dependencies
15 September 26 • 14 min read
Umbraco
Test-Driven Development (TDD) & Clean Architecture in Umbraco: Decoupling Domain Logic from CMS Dependencies

Umbraco makes it easy to ship fast. It also makes it easy to end up with business logic…

Read More
How Much Does an Umbraco 13 to 17 Upgrade Cost?
09 September 26 • 10 min read
Umbraco
How Much Does an Umbraco 13 to 17 Upgrade Cost?

Most agencies won't give you a straight answer. They'll say, "it depends" and leave you…

Read More