Let's say we have tens of java POJOs which represent my domain, that is, my data in the system which flows as objects between different layers of my system. The system may be a web application, or a simple desktop application. What the domain consists of doesn't really matter.
When designing my system, I am confused where I should put any validation logic. My POJOs ( domain objects ) represent my data, and some of the fields inside of those objects must adhere to certain criteria, but if I put a lot of validation logic inside my setter-methods, then the only way to tell the calling client is to throw an exception. And If I don't want the system to crash, the exception must be a checked exception which must be caught and handled. The consequence of that is that each and every time I create a new object using setter-methods (or even constructors), I have do either re-throw that exception or use try-catch block. It doesn't feel right to be forced to use try-catch on many setter-methods.
So the question is where I should put my validation logic, so that I don't clutter the my code with a lot of boilerplate try-catch blocks and rethrows. The finest JAVA byte eaters are welcome to join the discussion.
I have researched and googled, but not found any specific discussion on this topic, so I waiting with great enthusiasm to get some deeper insight into how things really should be done.
You might have answered your own question when you said
some of the fields inside of those objects must adhere to certain criteria
It always helps to think about the invariants in your system, i.e. the things you want to maintain at all cost or the rules that must always be followed.
Your POJOs are the "last line of defence" for such invariants on your data objects and thus an appropriate - or even necessary - place to put validation logic. Without such validation, an object might no longer represent something that makes sense in your domain.
These system invariants form a contract between your objects (or methods) and their "clients". If someone is trying to use them contrary to the (hopefully well-documented) contract, throwing an exception is the right thing to do, since it is the client's responsibility to use the individual parts of the system correctly.
Over time, I've started favoring unchecked exceptions over checked ones for any instances of contract violations, partly because of the reason you mention, namely to avoid try
-catch
blocks everywhere.
Java's standard unchecked exceptions include:
A best practice guideline is to use checked exceptions when an error is considered recoverable and unchecked exceptions otherwise.
Chapter 9 of Joshua Bloch's "Effective Java, 2nd ed." provides more wisdom on this topic:
None of the above should deter you from using any appropriate validation logic at higher levels, especially to enforce any context-specific business rules or constraints.