What does "validate" actually mean?
"Validation" is one of those terms that is a red flag for me, because explicit code for validation is in many cases a design smell.
Why is that the case? Because, in my opinion, the plausibility checks for an IBAN do not belong in an IBAN validator
that is linked to the framework, and certainly not in a configuration file, but preferably as explicit PHP code in an IBAN
class.
If someone asks which rules apply to an IBAN in the application, then it is a very straightforward procedure for me to search for the IBAN
identifier in the code and look there. In addition, we can also reuse an independent IBAN
class much more easily in another project.
Often we actually mean business rules when we talk about validation. For example, when we say "we only accept German IBANs", as we have already indicated, this is a business rule and not a validation rule. An IBAN is not invalid just because the bank account is in Austria. We may not accept it, but it is still technically valid. That is a business rule.
That's exactly why I avoid talking about validation. Instead, we should focus on creating objects that are technically relevant. Just like IBAN
.
Such business objects must always be valid and must never become invalid. This is precisely why we throw an exception in the constructor if we pass something to IBAN
that we don't think can be an IBAN. Yes, this is a value object.
With this value object, however, we make no statement as to whether an IBAN represents an actually existing bank account. Presumably only the bank itself can tell us that, and it may not want to do so for security reasons. In the best case, there is an online service that we ask for an IBAN and that tells us whether this IBAN is an existing account or not. In the worst case, we have to set up a process to check this ourselves. We could do this, for example, by transferring a cent to the account. If we receive this transfer back, then obviously no account with this IBAN exists.
Incidentally, this is an assumption that does not necessarily apply to every bank. A bank could also be happy about the unexpected windfall and put the cent in the employees' coffee coffers. But let's not get carried away here. For this example, we will therefore deliberately ignore the temporality of the world ("last week the account still existed ...").
We now have three classes of candidate IBANs, so to speak:
- Some string that cannot be a valid IBAN at all
- A string that looks like a valid IBAN
- An IBAN for which we know that a bank account exists
We cannot and do not want to do anything more with the first case, except perhaps display an error message somewhere that the user has entered an incorrect IBAN. This is the case where we throw an exception in the constructor of IBAN
, because we want to prevent an IBAN object
from having an invalid state.
Incidentally, this is one reason why we make value objects immutable, because an initially valid value remains valid by definition because and if it is not immutable.
In the second case, we would wrap a value object around this string, so to speak, which implicitly conveys the information "my content looks good". This is a real added value compared to working with scalar data.
That leaves the third case. If our IBAN object
semantically states that a string looks like a valid IBAN, how do we represent an IBAN for which we also know that a bank account exists?
Should we give the IBAN
object a status? However, this would contradict the immutability of value objects.