How do you architect systems that gracefully handle failure using asynchronous design patterns, message queues, and reliability engineering?