I completely understand what you're looking for and I don't say that there
is no ways, or patterns to do this, but considering the example, most of
the time it is better just to let the system live up, without any condense
control. this way, some people might get the error message "sorry, class
is full now" after they submit a request (even after initial checks that
let them enter the form for this request), but this makes system so
simple. Database is transactional, and any time you wanna add a new entry,
just check to see if thing are still OK or not.
Most of operation systems, databases, etc are like this. Its so nice just
to prevent any thing wrong (e.g. dead locks) to happen, but in 90% cases
the cost you pay to do all these checks is more than what you get. There
are cases system should be pessimistic, but the example below can be
considered no to be one of them