This might be asked a thousand times but couldn't find the answer. I just wonder how you people handle the business logic errors? Im trying to do a nice api for my business model. Some methods have quite a lot of validation and I just wonder how should I report them if something goes wrong? Returning true or false or null sometimes won't say what was the actual cause. Sometimes it works sometimes it is not enough. I read that some people use exceptions. I've thought that I could return error messages and when everything goes fine then it would be null. Somehow error codes just don't add up for me..
You are on the right track. Domain or business validation logic should do more than just report true or false. You want to enable the "user" of your business classes (ie the UI, or your logging system) to do something more than just report to the end-user that something is wrong.
Now, how you do that is completely up to you.
Decision to make: check everything and then report all errors/warnings/information, or stop at the first one you encounter. I would personally opt for the first one and let the UI decide how to use the info. There is a trade-off though: some validations may take longer than others and you may want to check these only if the "simple" ones succeed.
I wouldn't in any case use exceptions to do this. Some people use exceptions because most development environments have a nice way of showing any unhandled exceptions to the user. I think that is just a bad/lazy choice. And not just because it is then hard to decide where the error came from (though stack traces can help there), but also because it can(will) leave your app in an unpredictable state and may cause errors further down the line that will be even harder to debug.
Therefore, I tend to reserve exceptions for, well exceptional, circumstances. Things I didn't foresee. Validation doesn't fall into that category in my opinion - you did foresee all (current) validation concerns or you wouldn't have validated against them.
All in all, I code the validation in the domain/business classes to collect an array (or whatever I care to use) of error/warning/hint instances. They may be simple strings, they can be fully fledged classes. Usually I always return an instance to the caller and never an unassigned pointer. "Valid" is simplu indicated by a message count of zero.
There's a reason why. Exceptions handle exceptional situations very nicely. If your language has exceptions, start using them.
Invalid data should raise an exception.
The application can then collect all the data validation exceptions and display them to the user.
A function that either returns a proper value or sometimes returns a "status" value (line
That's because error "codes" are a very bad idea. Except in C programs where you have very little choice.
I usually use both a success flag and a list of messages, each of which is comprised of both a code and a freeform message. If you receive a successful response with messages then you know that the messages are all warnings and that something has happened on the server side.
The reason for using both codes and messages is so that a message can be displayed to a human reader but a code can be handled by software. This way, if the freeform message changes, software which handles that error doesn't also have to change.
Obviously if you are sure that there will never be a non-human reader of your responses, you can forget the codes but that's a difficult decision to undo later.
Have you read: Domain Driven Design ? This book has pretty good material on developing good domain models for isolating business rules.
When fixing bugs on projects, I tend to write down a user story for that piece of functionality to understand how it works from a users point of view. For example, yesterday, I was fixing a bug that caused an IllegalArgumentException if a certain combo box was not populated with the required data. The user would need to reset the device and start it up again. Now, fixing this problem may seem obvious, but the user story helped clear my thinking. I ended up pacifying that particular exception and ensuring the combo box had the text "data unavailable". So when the user raises a bug or calls support, the problem is better understood and the customer has not been left with some obscure error message. Little tweaks like this, seem to make a big difference when dealing with customers.