Or is it more practical to not model them at all with the type system and keep them as BigDecimal?
That depends: how to you want to mitigate the errors that arise when somebody tries to add a
Money to a
For the most part, your compute doesn't care at all about your domain model. It is just looking at registers, bytes in memory, and so on -- completely domain agnostic.
The design of your code servers your programmers, by helping them to avoid mistakes. Sometimes that help will be purely semantic -- tags to prevent the programmer from accidentally applying the domain concepts incorrectly. If you have automatic type checking, then there is a class of errors that you can eliminate, and you can take advantage of the type system to manage certain domain invariants for you as well.
We want fewer mistakes, because we want to be able to more accurately predict the cost of introducing new features into the code.
If everything is a BigDecimal, then generic checks have a very difficult time distinguishing correct from incorrect code, and you fall back toward other mitigation strategies (lots of testing; optimism).
Should there be a class for every currency?
It depends? Currencies are really different units in the same dimension, is the same way that meters and feet are different units of length, or degree Celsius and degree Fahrenheit are different units of temperature. Bugs that confuse the two can be very bad; on the other hand, there's not really much variation in behavior, so using a common
Money property with some currency code meta data might be more practical than trying to manage distinct types for each.