I do as much of this as I can with database constraints. Foreign key constraints, or check constraints, or even triggers if necessary (though I do try to avoid them).
Databases tend to outlive application code, or may be fronted by different applications (internal vs external for example). Keeping the constraints with the data is the best way to ensure that your data remains consistent within itself.