Worth noting the trade off in the ‘join in the app’ approach — you get a lot of data pulled in from your DB, and spend RAM and CPU juggling it in the app. I witnessed several occasions where the IO load on the DB was bringing it to its knees, so make sure to monitor network throughput and IOPS.
An opposing approach is to combine all relevant elements in each document. In your example, this means adding the ‘user’ document in every relevant ‘message’ and ‘post’. The missing ‘join’ operation becomes a simple filter, and secondary indices make it a piece of cake for the DB to handle. The trade off still exists — the collections are larger, data is duplicated and is more complex to update. Choose your battles :)