I find it helps a lot to think carefully about (1) message validation -- is this a valid message? (2) entity validation -- is this entity in a valid state? and (3) enterprise validation -- is the business system as a whole now in a valid state? These three questions map on to what is often an ACL, domain, and domain services layer. At the end of the day there's going to be corner cases though in which case as a rule of thumb there's something to be said for doing validation at the edges and moving it in closing to the center as needed. In my experience when it's not clear where validation belongs that often means nobody really understands that validation (or that validation is even wrong and is not what the business wants -- it happens!) and so there's something to be said for keeping it out of the core domain.
That's a good approach unless a validation requires a db call with inner join (for example). This becomes too costly to do it 5 times (for example) just to get something written into db.
A interface to define the validation aspect. 3 separate implementations of said interface : 1 for db lookup, 1 for cache lookup and last for unit tests. 1 factory method which return the instance to use depending of context. Another temporal cache that holds the instance of said interface (after all, you can never have enough caches).
And all of a sudden, validating some single shit takes +300 lines of code (plus tests). Welcome to enterprise , clean, better designed, greatly arhitected software everyone....
Range acceptance - could defiantly be a business rule. Cross-field consistency - Could also be a business rule.
I implement these rules both sides. Ideally i don't want an invalid command sent off in the first place. I also don't want badly implemented UI corrupting my business data.
This way i can provide instant feedback to the user, and also protect the data in case the UI is badly implemented.
The main point of these architectures is that you can use them from many user interfaces.
Adhering to dry in all cases, is not always the best solution. As the software community has discovered over the past years. Especially in relation to microservices.
In my software there's a bit of a disconnect between the application and interactors. As there is a message queue between them.
Business rules does not mean what the business (bosses) dictates that the app should have.
Business rules the rules for the business domain.
Whether you should escape sql (or even use sql) is a technical/application concern, not a business rule.
A business rule would be something like "customers must get 10% discount if they bought more than 100 units" or "no employee should be allowed to have a salary bigger than their manager", etc.
Business rule validation(E.g student can't be assigned more than 10 courses) is done on the entities themselves.
You put it on both sides to get the best of both. Instant feedback to the user, and on entities as last resort protection against a badly implemented interface.
In two places in UI: as close to the user as possible (which improves ergonomics) and at the system's border (which prevents entering invalid data to the system at all). While the former is somewhat optional, the latter is absolutely necessary and cannot be left to client-side JavaScript.