Grails has a rich set of tools for performing validations of your domain objects. There are quite a few built in validations that you can use. These mainly resolve around validating single properties within a single domain object. It also supports things like validating the sizes of dependent collections.
Sometimes you need to know about the properties of one instance to validate another dependent instance. The property of that dependent instance might be used to determine the valid values of or might provide a limit for a property.
This is more easily explained with an example.
Problem StatementI'll use voting for members of the Board of a public company as an example.
A ShareHolder owns N number of shares of stock in a company. When the ShareHolder votes on a Board member of the company, the ShareHolder can assign 1 to N shares to that Candidate. The top 5 Candidates that receive the most Shares (not individual votes) win the election.
We might create a series of Domain classes like this:
A ShareHolder can vote multiple times, so it has many Votes. A Candidate can be voted for many times, so it has many Votes. A given Vote can be assigned 1 or more shares. But the shares for a given vote must be less than or equal to the number of shares that the voter (the ShareHolder) owns.
ValidationThe natural place to perform this validation is on the Vote domain object itself. The problem is that the limit is really the shares value from the ShareHolder domain object. To solve this we can not use a built in validation. Instead we need to create our own.
To do this we can utilize the validator validation. This is no different than any other custom validation that we might need to create. Since we must have the ShareHolder and the Candidate set for this to be a valid object, we can assume that they are available. Because of this we should be able to access their properties to validate our Vote domain object.
Add a custom validator constraint:
As you can see, the obj that gets passed in is the instance that is being validated. The val is the value of the property being validated, in this case the voteShares property. All that has to be done is to compare the value of the obj.shareHolder?.shares too the voteShares property. The obj.shareHolder?. should protect us against a null reference in case the shareHolder isn't set. Then the validation will just fail on not having the ShareHolder.
Nothing really tricky here, just something interesting I ran into recently.