Not all resources are transactional. Email is one of them. So one cannot expect sending email is rolled back when other resources fails in a transaction.
There are some alternative solutions:
1) invoke non-transactional resource at the last step.
Just like you did in the create user example. In this case, all business constraints are checked and pass, the only reason of failure is infrastructure issue which does not frequently happen in real world(with carefully maintainence). Make some compensation when the infrastructure failure does happen. This could be done automatically or manually, depends on how often it happens. For example, if the email is sent, but the user setup is rolled back, you may tell him/her that you're very sorry(this is very import :P ) but there is something wrong with the setup, please try again.
2) apply eventually consistency
Use transactional resource instead to trigger the process. For example, you may use messaging (globally transactional if using 2 phase commit) to notify to send emails.
CreateUser(...)
{
//1.) New up user object
//2.) Add newly created object to database
//3.) Publish user setup event by messaging
//4.) Commit transaction ( ensures message is successfully sent AND object is created in database, else transaction fails
}
In any case, failures and unconsistency occurs more or less. You need to evalute is it acceptable.