Typical Usage: Enforcing Constraints

The caller will typically obtain the target object (eg from some repository) and then use the injected WrapperFactory to wrap it before interacting with it.

For example:

public class CustomerAgent {
    @Action
    public void refundOrder(final Order order) {
        final Order wrappedOrder = wrapperFactory.wrap(order);
        try {
            wrappedOrder.refund();
        } catch(InteractionException ex) {          (1)
            messageService.raiseError(ex.getMessage());  (2)
            return;
        }
    }
    ...
    @Inject
    WrapperFactory wrapperFactory;
    @Inject
    MessageService messageService;
}
1 if any constraints on the Order’s `refund() action would be violated, then …​
2 …​ these will be trapped and raised to the user as a warning.

Usage Notes

Domain Objects

For domain objects (not mixins), the wrapper can be interacted with as follows:

  • a get…​() method for properties or collections

  • a set…​() method for properties

  • any action

with CAUSEWAY-3084, addTo…​() and removeFrom…​() methods for collections were removed; the notion (direct) collection modification was deprecated as any specific business logic should by handled via actions instead;

Calling any of the above methods may result in a (subclass of) InteractionException if the object disallows it. For example, if a property is annotated with @ActionLayout#hidden then a HiddenException will be thrown. Similarly if an action has a validateXxx() method and the supplied arguments are invalid then an InvalidException will be thrown.

In addition, the following methods may also be called:

An exception will be thrown if any other methods are thrown.

If the interface is performed (action invoked or property set), then - irrespective of whether the business rules were checked or skipped - a command will be created and pre- and post-execute domain events) will be fired.

Mixins

For mixins, the behaviour of the wrapper is similar but simpler. Mixin wrappers only apply to actions, and so the wrapper will enforce the hidden/disable/validate rules before executing. In addition, any default…​(), choices…​() or autoComplete…​() methods can be called.

Other Use Cases

Creating interactions

One use case for the WrapperFactory is to invoke actions that have Action#executionPublishing enabled. This will result in a serializable representation of the action invocation to be published; this could then be used to route a message to another system.

The target action is often a no-op; indeed it can even be hidden if the WrapperFactory is invoked with rules skipped.

Integration tests

The WrapperFactory is frequently used within integration tests. A longer discussion of the use of the WrapperFactory within integration tests can be found here.

Trust boundaries

If there is a (lack of) trust boundary between the caller and callee — eg if they reside in different modules — then the WrapperFactory is one mechanism to ensure that any business constraints defined by the callee are honoured.

For example, if the calling object attempts to modify an unmodifiable property on the target object, then an exception will be thrown.

Said another way: interactions are performed "as if" they are through the viewer.

Listener API

One possible use case for the listener API would be to autogenerate documentation, for example a sequence diagram from tests.

Such a feature would probably also use InteractionContext, which builds up an execution call graph of interactions between (wrapped) objects.