Specification pattern
The interfaces and classes listed in this chapter provide support for the Specification
pattern, as described in Eric Evans' book Domain Driven Design, p224.
Apache Causeway will automatically apply such specifications as validation rules on properties (as per @Property#mustSatisfy()) and on action parameters (as per @Parameter#mustSatisfy()).
Specification
The heart of the support for this pattern is the Specification
interface:
public interface Specification {
String satisfies(Object obj); (1)
}
1 | if returns null , then the constraint is satisfies; otherwise returns the reason why the constraint has not been satisfied. |
For example:
public class StartWithCapitalLetterSpecification implements Specification {
public String satisfies(Object proposedObj) {
String proposed = (String)proposedObj; (1)
return "".equals(proposed)
? "Empty string"
: !Character.isUpperCase(proposed.charAt(0))
? "Does not start with a capital letter"
: null;
}
}
public class Customer {
@Property(mustSatisfy=StartWithCapitalLetterSpecification.class)
public String getFirstName() { /* ... */ }
...
}
1 | this ugly cast can be avoided using some of the other classes available; see below. |
Specification2
The Specification2
interface extends the Specification
API to add support for i18n.
This is done by defining an additional method that returns a translatable string:
public interface Specification2 extends Specification {
public TranslatableString satisfiesTranslatable(Object obj); (1)
}
1 | if returns null , then the constraint is satisfies; otherwise returns the reason why the constraint has not been satisfied. |
Note that if implementing Specification2
then there is no need to also provide an implementation of the inherited satisfies(Object)
method; this will never be called by the framework for Specification2
instances.
Adapter classes
The AbstractSpecification
and AbstractSpecification2
adapter classes provide a partial implementation of the respective interfaces, providing type-safety.
(Their design is modelled on the TypesafeMatcher
class within Hamcrest).
For example:
public class StartWithCapitalLetterSpecification extends AbstractSpecification<String> {
public String satisfiesSafely(String proposed) {
return "".equals(proposed)
? "Empty string"
: !Character.isUpperCase(proposed.charAt(0))
? "Does not start with a capital letter"
: null;
}
}
public class Customer {
@Property(mustSatisfy=StartWithCapitalLetterSpecification.class)
public String getFirstName() { /* ... */ }
...
}
The AbstractSpecification2
class is almost identical; its type-safe method is satisfiesTranslatableSafely(T)
instead.
Combining specifications
There are also adapter classes that can be inherited from to combine specifications:
-
SpecificationAnd
- all provided specifications' constraints must be met -
SpecificationOr
- at least one provided specifications' constraints must be met -
SpecificationNot
- its constraints are met if-and-only-if the provided specification’s constraint was not met.
Note that these adapter classes inherit Specification
but do not inherit Specification2
; in other words they do not support i18n.