I'm using .NET so I've gone with using exceptions.
Specifically though, I've created a DomainException class (there are a couple of super-specific exceptions that inherit from it but generally it is good enough on it's own). However, because it's being raised by domain logic, it has a few extra properties. In addition to the Message (what happened/went wrong e.g. "Unable to place order"), it has the following:
// Why did the exception happen? (e.g. "Order has already been cancelled")
string Reason { get; }
// What could the user do about it? (e.g. "Create a new order")
string[] Recommendations { get; }
// What could a software support person do? (Not surfaced in the UI, only logs. e.g. Configuration to check)
string[] AdvancedRecommendations { get; }
// A unique ID in case the exception is returned over WCF as a FaultException or sent as a message, so software support people can find the exception in client/server logs etc
Guid Id { get; }
These exceptions are thrown from within the Entities themselves (often when invariant checking fails), and bubble up the "stack". In the case of a webapp, they'd be an error dialog or page. In the case of a "smart client" they'd be converted to a FaultException<DomainFault> by using a custom WCF IErrorHandler and converted back and re-thrown on the client.
In addition, if it's useful, there's nothing stopping you sending a failure message (event?) onwards. Particularly if you're running as a downstream event processor - you can't really bubble exceptions backwards because the point is you're handling an event that HAS ALREADY HAPPENED. The always have to move forwards, even if that's notifying of a failure rather than a success.
HTH.