A business rule is a domain condition that decides whether an operation is valid or how it proceeds. Rules should grow with business complexity, but continuously adding them to an existing class or method eventually makes that code hard to maintain. section 2.3 section 1.2.4
Keep a rule in the operation or entity when it is simple and uses information already available there. Move complex rules into a dedicated validator or rule class when they need other data, have several conditions, or would make the existing object responsible for too much. section 2.3 section 3.2.4
Validating a training registration
Registering an employee can require checking available spots, prior course participations, and duplicate registration. An offering can check its own capacity and current attendees, but its past-participation rule requires training data. A service retrieves the employee and offering, delegates the decision to a validator, and adds the employee only when validation has no errors. section 3.2.4
Listing 3.6 AddEmployeeToOfferingValidator class
class AddEmployeeToOfferingValidator {
private TrainingRepository trainings;
public ValidationResult validate(
Offering offering,
Employee employee) {
var validation = new ValidationResult();
if(!offering.hasAvailableSpots()) { #1
validation.addError("Offering has no available spots.");
}
var timesParticipantTookTheTraining =
trainings.countParticipations(employee, offering.getTraining())
if(timesParticipantTookTheTraining >= 3) { #2
validation.addError("Participant can't take the training again.");
}
if(offering.isEmployeeRegistered(employee)) { #3
validation.addError("Participant already in this offering.");
}
return validation;
}
}
The validator gathers multiple failures in one validation result: no available spots, three prior participations, and an existing registration. It keeps decision logic separate from the service’s retrieval and update flow. section 3.2.4
Generalized rules should receive generic data rather than hardcode concrete values. Separate data retrieval from rule execution: a repository-oriented class can fetch the data and construct or invoke a reusable rule. Start with simple glue code when it is easy to understand and evolve; add more flexibility only when new variations justify its cost. section 5.2.1



