Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

Actions have four parts to them (responsibliities, I suppose):

  • execution
  • supporting methods for perform the validation etc
  • target object that they act upon
  • event publishing

Our programming model could provide several syntaxes to put these responsibilities in different places.  It already supports two (standard actions, and mixins).To compare these currently provides two different syntaxes: standard, and mixins.  This page recaps on those options and suggests several others.  To compare the syntaxes, we'll use a concrete example:

...

Code Block
public class Customer {                                                              // target

    @Action
    public staticCustomer class PlaceOderEvent extends ActionDomainEvent<Customer> {}  placeOrder(Product p, int quantity) { ... }                      // action execution

    public boolean hidePlaceOrder() {     

... }    @Action(domainEvent = PlaceOrderEvent.class)                                     // eventsupporting publishingmethods
    public CustomerString placeOrder(Product p, int quantitydisablePlaceOrder() { ... }
    public String disable1PlaceOrder(Product p) { ... }
    public Collection<Product> choices0PlaceOrder() {     // action execution
... }
    public booleanProduct hidePlaceOrderdefault0PlaceOrder() { ... }
    public int default1PlaceOrder() {                                   // supporting methods... }
    public String disablePlaceOrdervalidate1PlaceOrder(int quantity) { ... }
    public String disable1PlaceOrdervalidatePlaceOrder(Product p), { ... }
    public Collection<Product> choices0PlaceOrder(int quantity) { ... }
    public Product default0PlaceOrder() { ... }
    public int default1PlaceOrder() { ... }
    public String validate1PlaceOrder(int quantity) { ... }
    public String validatePlaceOrder(Product p, int quantity) { ... }
}

...

}


Mixins syntax

Mixins Mixins change the target, by allowing this set of methods to be moved to a different object.  In other words, the target responsibility changes.

...

Code Block
@Action                                                        
public class Customer_placeOrder() {                           

    private final Customer target;                                                // target
    public Customer_placeOrder(Customer target) { ... }

    public staticCustomer class PlaceOrderEvent extends ActionDomainEvent<Customer> {}  act(Product p, int quantity) { ... }                          // action execution

    public boolean hideAct() { ... }      

    @Action(domainEvent = PlaceOrderEvent.class)                                  // eventsupporting publishingmethods
    public CustomerString act(Product p, int quantitydisableAct() { ... }
    public String disable1Act(Product p) { ... }
    public Collection<Product> choices0Act() { ... }
    public Product default0Act() // action execution
{ ... }
    public booleanint hideActdefault1Act() { ... }
    public String                                         // supporting methods
    public String disableAct(validate1Act(int quantity) { ... }
    public String disable1ActvalidateAct(Product p), { ... }
    public Collection<Product> choices0Act(int quantity) { ... }
    public Product default0Act() { ... }
    public int default1Act() { ...}
    public String validate1Act(int quantity) { ... }
    public String validateAct(Product p, int quantity) { ... }
}

Notes:

}

Notes:

  • Instead of @Action, Instead of @Action, the @Mixin(method="act") could also be used, with additional annotations on the act(...) method.  I've chosen the version with the least boilerplate here.
  • Mixins are also used for derived properties or collections (ie query-only actions with no side-effects).  These are specified using @Property or @Collection 
  • We now have two classes here: the mixin, and the domain event within.

Parameters syntax (proposed)

Per this thread on slack, we could introduce a Parameters object (in Java 14+, this might be a record) to bring together all of the parameters into a single object.  This would make it easier to avoid issues with numbering etc.

This syntax changes the way in which supporting methods are associated back to the main execution method.

Analysis

Actions have three parts (or responsibilities) to them:

  • execution
  • the target object that they act upon
  • the set of parameters/arguments that are passed to the execution and to the supporting methods that perform validation etc.

(Actually, there's also event publishing, and an earlier version of this page also discussed that ... but we didn't see any point in changing how that worked).

The standard model and the mixin model have a quite different "feel" to them, though they only subtly change where these responsibilities reside: for the standard model, the target object is implicit (ie "this") whereas with mixins the target object is explicit (passed into the constructor).  In other respects the programming models are the same.

Playing around with where these responsibilities live allow us to create a number of other programming models.  The table below summarises and names these options::


targetbehaviourparameter
values
Notes
standardimplicitY
The target is implicit ("this"), and the set of parameter values (arguments) are only implicit in the signatures of the execute action and the supporting methods
mixinsYY
The target is explicit, being the constructor of the mixin.
Parameters model

YSeparate class that captures the set of parameters that are passed to the supporting methods
Parameters on Act

YMinor variation
Parameters Everywhere

YAnother variation
Mixins + ParametersYYYCombines the concepts of a mixin along with a parameters model
Targetless Mixins +
Targeted Parameters

Y
Y
Y
Splits out state and behaviour
Command handlers 
Commands

Y
Y
Y
Variation that splits behaviour into separate interfaces

The rest of the page describes these options in more detail.

Parameters model syntax (proposed)

Per this thread on slack, we could introduce a Parameters object (in Java 14+, this might be a record) to bring together all of the parameters into a single object.  This would make it easier to avoid issues with numbering etc.

This syntax changes the way in which supporting methods are associated back to the main execution method.

Code Block
languagejava
public class Customer {                                                           // target

    @Value @Accessors(fluent = true)                   
    public class PlaceOrderParameters {                                           // to assist supporting methods 
        Product product;
        int quantity;
    }

    @Action
    public Customer placeOrder(Product p, int quantity) { ... }                   // execution    

    public boolean hidePlaceOrder() { ... }                                       // supporting methods use PlaceOrderParameters
    public String disablePlaceOrder() { ... }
    public String disable1PlaceOrder(PlaceOrderParameters params) { ... }
    public Collection<Product> choices0PlaceOrder() { ... }              
    public Product default0PlaceOrder() { ... }
    public int default1PlaceOrder() { ... }
    public String validate1PlaceOrder(PlaceOrderParameters params) { ... }
    public String validatePlaceOrder(PlaceOrderParameters params) { ... }
}

Notes:

  • The @Value @Accessors(fluent=true) allows us to use a syntax that is very similar to Java 14 records.
  • There is some duplication here: the list of the parameter types appears both in the placeOrder(...) method, as well as in the PlacerOrdersParameters class.

The above would also be supported with mixins:

Code Block
@Action
public class Customer_placeOrder {

    private final Customer target;                                                // target
    public Customer_placeOrder(Customer target) { ... }

    @Value @Accessors(fluent = true)             
    public static class PlaceOrderParameters {                                    // to assist supporting methods
        Product product;
        int quantity;
    }

    public Customer act(Product p, int quantity) { ... }                          // execution

    public boolean hideAct() { ... }                                              // supporting methods
    public String disableAct() { ... }
    public String disable1Act(PlaceOrderParameters params) { ... }
    public Collection<Product> choices0Act() { ... }              
    public Product default0Act() { ... }
    public int default1Act() { ... }
    public String validate1Act(PlaceOrderParameters params) { ... }
    public String validateAct(PlaceOrderParameters params) { ... }
}

Notes:

  • we now have three classes here: the mixin, the domain event, and the parameters object.

Parameters on Act syntax (proposed)

This is a variant of the previous, but uses the parameters class in the action as well:

Code Block
public class Customer {                                                           // target

    @Value @Accessors(fluent = true)                   
    public class PlaceOrderParameters {                                           // to assist supporting methods 
        @Parameter() @MemberOrder(1)
        Product product;
        @Parameter() @MemberOrder(2)
        int quantity;
    }

    @Action
    public Customer placeOrder(PlaceOrderParameters params) { ... }               // execution    

    public boolean hidePlaceOrder() { ... }                          
Code Block
languagejava
public class Customer {                                                           // target

    @Value @Accessors(fluent = true)                   
    public class PlaceOrderParameters {                                           // to assist supporting methods 
        Product product;
        int quantity;
    }

    public static class PlacerOrderEvent extends ActionDomainEvent<Customer> {}                                  // supporting methods use PlaceOrderParameters
    public String disablePlaceOrder() { ... }
    public String @Action(domainEvent = PlaceOrderEvent.class)   disable1PlaceOrder(PlaceOrderParameters params) { ... }
    public Collection<Product> choices0PlaceOrder() { ... }              
    public Product default0PlaceOrder() { ... }
    public int   // event publishingdefault1PlaceOrder() { ... }
    public CustomerString placeOrdervalidate1PlaceOrder(Product p, int quantityPlaceOrderParameters params) { ... }
    public String validatePlaceOrder(PlaceOrderParameters params) { ... }
}

Notes:

  • this removes the duplication between the placeOrder(...) parameter list and the list of members in PlaceOrderParameters class.
  • the @Parameter and @MemberOrder syntax would be required by the framework to identify PlaceOrderParameters as a container of parameters (as opposed to a reference object or custom value type)

As a mixin, this becomes:

Code Block
@Action
public class Customer_placeOrder {

    private final // execution    

Customer target;    public boolean hidePlaceOrder() { ... }                                       // supporting methods use PlaceOrderParameterstarget
    public String disablePlaceOrder(Customer_placeOrder(Customer target) { ... }
    public String disable1PlaceOrder(PlaceOrderParameters params) { ... }
    public@Value Collection<Product> choices0PlaceOrder() { ... } @Accessors(fluent = true)             
    public static Productclass default0PlaceOrder()PlaceOrderParameters { ... }
            public int default1PlaceOrder() { ... }
    public String validate1PlaceOrder(PlaceOrderParameters params) { ... }
    public String validatePlaceOrder(PlaceOrderParameters params) { ... }
}

Notes:

  • The @Value @Accessors(fluent=true) allows us to use a syntax that is very similar to Java 14 records.
  • There is some duplication here: the list of the parameter types appears both in the placeOrder(...) method, as well as in the PlacerOrdersParameters class.

The above would also be supported with mixins:

Code Block
@Action
public class Customer_placeOrder {

    private final Customer target;                   // to assist supporting methods
        @Parameter()
        Product product;
        @Parameter()
        int quantity;
    }

    @Action
    public Customer act(PlaceOrderParameters params) { ... }                             // targetexecution

    public boolean Customer_placeOrder(Customer targethideAct() { ... }

    @Value @Accessors(fluent = true)             
    public static class PlaceOrderParameters {                  // supporting methods
    public String disableAct() { ... }
    public String  // to assist supporting methodsdisable1Act(PlaceOrderParameters params) { ... }
    public    Product product;
        int quantity;
Collection<Product> choices0Act() { ... }    }

    public static class PlaceOrderEvent extends ActionDomainEvent<Customer> {}
    public Product default0Act() { ... }
    public int default1Act() { ... }
    public String validate1Act(PlaceOrderParameters params) { ... }
    public String validateAct(PlaceOrderParameters params) { ... }
}

Notes:

  • we still have three classes here (mixin, parameters and domain event), but we have removed the duplication between the act(...) parameter list and the list of members of PlaceOrderParameters class

Parameters everywhere syntax (proposed)

The previous syntax only passes in parameters to some of the supporting methods.  For consistency, we could imagine it being passed in always.

Just focusing on the mixin syntax, this would become:

Code Block
@Action
public class Customer_placeOrder {

    @Action(domainEvent = PlaceOrderEvent.class)                                  // event publishing
    public Customer act(Product p, int quantity) { ... }                          // execution

    publicprivate booleanfinal hideAct() { ... }Customer target;                                                // supporting methodstarget
    public String disableAct(Customer_placeOrder(Customer target) { ... }

    public@Value String disable1Act(PlaceOrderParameters params) { ... }@Accessors(fluent = true)             
    public static Collection<Product>class choices0Act()PlaceOrderParameters { ... }              
    public Product default0Act() { ... }
    public int default1Act() { ... }
  // to publicassist String validate1Act(PlaceOrderParameters params) { ... }
    public String validateAct(PlaceOrderParameters params) { ... }
}

Notes:

  • we now have three classes here: the mixin, the domain event, and the parameters object.

Parameters on Act syntax (proposed)

This is a variant of the previous, but uses the parameters class in the action as well:

Code Block
public class Customer {  supporting methods
        @Parameter()
        Product product;
        @Parameter()
        int quantity;
    }

    @Action
    public Customer act(PlaceOrderParameters params) { ... }                      // targetexecution

    public @Valueboolean @Accessors(fluent = true)hideAct(PlaceOrderParameters params) { ... }                   // supporting methods
    public classString disableAct(PlaceOrderParameters params) { ... }
    public String disable1Act(PlaceOrderParameters params) { ... }
    public Collection<Product> choices0Act(PlaceOrderParameters params) { ... }              
    public Product  // to assist supporting methods 
  default0Act(PlaceOrderParameters params) { ... }
    public int @Parameterdefault1Act(PlaceOrderParameters params) @MemberOrder(1)
 { ... }
    public String validate1Act(PlaceOrderParameters params) { Product product;... }
    public String   @ParametervalidateAct()PlaceOrderParameters @MemberOrder(2params)
 { ... }
}

Discussion

With the parameters object passed in everywhere, I could see myself starting to move functionality onto that object.  So as an idiom, we might see the following sort of code (in a mixin):

Code Block
@Action
public class Customer_placeOrder {

    private final Customer target;      int quantity;
    }

    public static class PlacerOrderEvent extends ActionDomainEvent<Customer> {}                                      

    @Action(domainEvent = PlaceOrderEvent.class)                                  // event publishing
    public Customer _placeOrder(PlaceOrderParametersCustomer paramstarget) { ... }

    @Value @Accessors(fluent = true)         // execution    

    public static booleanclass hidePlaceOrder()PlaceOrderParameters { ... }                                       // supporting methods use PlaceOrderParameters
 see below.                    public String disablePlaceOrder() { ... }
    public String disable1PlaceOrder(PlaceOrderParameters params) { ... }

    public static Collection<Product>class choices0PlaceOrder()PlaceOrderEvent {extends ...ActionDomainEvent<Customer> {}              
    public Product default0PlaceOrder() { ... }
    public int default1PlaceOrder() { ... }
    public String validate1PlaceOrder(PlaceOrderParameters params) { ... }

    @Action
    public StringCustomer validatePlaceOrderact(PlaceOrderParameters params) { ... }
}

Notes:

  • this removes the duplication between the placeOrder(...) parameter list and the list of members in PlaceOrderParameters class.
  • the @Parameter and @MemberOrder syntax would be required by the framework to identify PlaceOrderParameters as a container of parameters (as opposed to a reference object or custom value type)

As a mixin, this becomes:

Code Block
@Action
public class Customer_placeOrder {

    private final Customer target;                                  return params.act(this); } 

    public boolean hideAct(PlaceOrderParameters params) { return params.hide(this); }                                              
    public String disableAct(PlaceOrderParameters params) { return params.disable(this); }
    public String disable1Act(PlaceOrderParameters params) { return params.disable1(this); }
    public Collection<Product> choices0Act(PlaceOrderParameters params) { return params.choices0(this); }              // target
    public Product Customer_placeOrderdefault0Act(CustomerPlaceOrderParameters targetparams) { return ...params.default0(this); }

    @Valuepublic int @Accessors(fluent = true) default1Act(PlaceOrderParameters params) { return params.default1(this); }
    public String validate1Act(PlaceOrderParameters params) { return   params.validate1(this); }
    public staticString class PlaceOrderParametersvalidateAct(PlaceOrderParameters params) { params.validate(this); }
}

which would then beef up the parameters object:

Code Block
@Action
public class Customer_placeOrder {

    private final Customer target;                        // to assist supporting methods
        @Parameter() @MemberOrder(1)
    // target
   Product product; ...

       @Value @Parameter@Accessors() @MemberOrder(2)
fluent = true)        int quantity;
    }

    public static class PlaceOrderEvent extends ActionDomainEvent<Customer>PlaceOrderParameters {}                                      

    @Action(domainEvent = PlaceOrderEvent.class)  @Parameter()
        Product product;
        @Parameter()
               // event publishing
int quantity;

	    public Customer act(PlaceOrderParametersCustomer paramscustomer) { ... }                             // execution

    	public boolean hideAct(Customer customer) { ... }             ... }                  // supporting methods              // supporting methods
	    public String disableAct(Customer customer) { ... }
	    public String disable1Act(PlaceOrderParametersCustomer paramscustomer) { ... }
	    public Collection<Product> choices0Act(Customer customer) { ... }              
	    public Product default0Act(Customer customer) { ... }
	    public int default1Act(Customer customer) { ... }
	    public String validate1Act(PlaceOrderParametersCustomer paramscustomer) { ... }
	    public String validateAct(PlaceOrderParametersCustomer paramscustomer) { ...}
  }
}

Notes:

  • we still have three classes here (mixin, parameters and domain event), but we have removed the duplication between the act(...) parameter list and the list of members of PlaceOrderParameters class

Parameters everywhere syntax (proposed)

The previous syntax only passes in parameters to some of the supporting methods.  For consistency, we could imagine it being passed in always.

Just focusing on the mixin syntax, this would become:

Code Block
@Action
public class Customer_placeOrder {

    private final Customer target;     }

    ...

    @Action
    public Customer act(PlaceOrderParameters params) { return params.act(this); }                         // remainder is just boilerplate

    public boolean hideAct(PlaceOrderParameters params) { return params.hide(this); }                                             // target
    public String Customer_placeOrderdisableAct(CustomerPlaceOrderParameters targetparams) { return ...params.disable(this); }

    @Valuepublic String @Accessorsdisable1Act(fluentPlaceOrderParameters = trueparams) {            return params.disable1(this); }
    public staticCollection<Product> class PlaceOrderParameterschoices0Act(PlaceOrderParameters params) { return params.choices0(this); }              
    public Product default0Act(PlaceOrderParameters params) { return params.default0(this); }
    public int default1Act(PlaceOrderParameters params) //{ to assist supporting methodsreturn params.default1(this); }
    public String validate1Act(PlaceOrderParameters params) { return @Parameterparams.validate1(this); @MemberOrder(1)}
    public String validateAct(PlaceOrderParameters params) { params.validate(this); }
}

Notes:

  • the target is still outside of the parameters object
  • Event publishing also outside
  • Everything else has moved inside the parameters object
  • This implies that we would need dependency injection for the parameters object
  • The rest of the code in the mixin is just boilerplate.  It's possible that the Lombok @Delegate annotation might be used to remove some of this boilerplate, didn't investigate further.

Mixins and Parameters combined (proposed)

The previous section describes an idiom to work within the new Parameter object programming model.  But the next step along the journey would be to formally recognise this pattern.  This would amount to collapsing the mixin concept and the parameters concept into the same thing.  Said another way, mixins start to become stateful, keeping track of the parameter argument values as well as the target object:

Code Block
@Action
public class Customer_placeOrder {

    private final Customer target;              Product product;
        @Parameter() @MemberOrder(2)
        int quantity;
    }

    public static class PlaceOrderEvent extends ActionDomainEvent<Customer> {}                                 // target
    ...

    @Action@Parameter(domainEvent = PlaceOrderEvent.class)                  ) @MemberOrder(1)                // event publishing
    public Customer act(PlaceOrderParameters params) { ... }                      // execution

    public boolean hideAct(PlaceOrderParameters params) { ... }  // supporting methods support
    Product product;
    @Parameter() @MemberOrder(2)
    int quantity;

  // supporting methods@Action
    public StringCustomer disableActact(PlaceOrderParameters params) { ... }
         public String disable1Act(PlaceOrderParameters params) { ... }
    public Collection<Product> choices0Act(PlaceOrderParameters params) { ... }              
    public Product default0Act(PlaceOrderParameters params) { ... }// execution 

    public intboolean default1ActhideAct(PlaceOrderParameters params) { ... }
          public String validate1Act(PlaceOrderParameters params) { ... }
    public String validateAct(PlaceOrderParameters params) { ... }
}

Discussion

With the parameters object passed in everywhere, I could see myself starting to move functionality onto that object.  So as an idiom, we might see the following sort of code (in a mixin):

Code Block
@Action
public class Customer_placeOrder {

    private final Customer target;        // supporting methods
    public String disableAct() { ... }
    public String disable1Act() { ... }
    public Collection<Product> choices0Act() { ... }              
    public Product Customer_placeOrder(Customer targetdefault0Act() { ... }

    public @Valueint @Accessorsdefault1Act(fluent) = true) { ... }
    public String validate1Act() { ... }
   
 public String validateAct() public{ static class PlaceOrderParameters { ... }                                       // see below.                                   

    public static class PlaceOrderEvent extends ActionDomainEvent<Customer> {}                                      

    @Action(domainEvent = PlaceOrderEvent.class)                                  
    public Customer act(PlaceOrderParameters params) { return params.act(this); } 

    public boolean hideAct(PlaceOrderParameters params) { return params.hide(this); }... }
}

Notes:

  • here the supporting methods would simply read from the fields of the mixin that represent the parameters of the mixin itself.
  • the domain event class is still separate
  • @MemberOrder is required because the JVM does not guarantee the order in the bytecode is the same as in the source file.

Target-less Mixins + Targeted Parameters

Traditionally mixins hold all of the behaviour and a little bit of the state - namely the target object.    Meanwhile parameters hold the rest of the state, but without the target.

Another way to divide the responsibilities would be to move the target from the mixin, and add it into the parameters object.  In other words, the former would just be the behaviour, the latter would be just the state.

Thus we have an extended parameters object, that also takes the target:

Code Block
@Value @Accessors(fluent = true)             
public class PlaceOrderParameters {
    @Target                          // a new  annotation
    public String disableAct(PlaceOrderParameters params) { return params.disable(this); }
    public String disable1Act(PlaceOrderParameters params) { return params.disable1(this); }Customer customer;

    @Parameter()
    public Collection<Product> choices0Act(PlaceOrderParameters params) { return params.choices0(this); }Product product;
    @Parameter()
    int quantity;
}

Meanwhile the mixin provides just the behaviour, of both the action and also the various supporting methods.  The supporting methods all need to take the PlaceOrderParameters, because it now contains the target, at least 

Code Block
@Action
public class    Customer_placeOrder {

     @Action
    public ProductCustomer default0Actact(PlaceOrderParameters params) { return params.default0(this); }
... }            public int default1Act(PlaceOrderParameters params) { return params.default1(this); }
    public String validate1Act(PlaceOrderParameters params) { return params.validate1(this); } // execution

    public Stringboolean validateActhideAct(PlaceOrderParameters params) { params.validate(this); }
}

which would then beef up the parameters object:

Code Block
@Action
public class Customer_placeOrder {

... }        private final Customer target;               // supporting methods
    public String disableAct(PlaceOrderParameters params) { ... }
    public String disable1Act(PlaceOrderParameters params) { ... }
    //public target
Collection<Product> choices0Act(PlaceOrderParameters params) { ...

 }    @Value @Accessors(fluent = true)       
    public Product default0Act(PlaceOrderParameters params) { ... }
    public staticint class PlaceOrderParametersdefault1Act(PlaceOrderParameters params) { ... }
    public String validate1Act(PlaceOrderParameters params) { ... }
    public String validateAct(PlaceOrderParameters params) { ... }
}

Command Handlers

Building on the previous example, having split up the behaviour from the state completely, we realise that there's no need to keep all of the methods of the mixin together.

We could rename the "parameters object" as a command:

Code Block
@Value @Accessors(fluent = true)            

  
public class PlaceOrderCommand {   @Parameter() @MemberOrder(1)
    @Target     Product product;
        @Parameter() @MemberOrder(2)
        int quantity;

	    public Customer act(Customer customer);

 { ... } @Parameter()
    Product product;
    @Parameter()
    int quantity;
}

and then we could have a number of handlers, for example for the execution:

Code Block
public class CustomerPlaceOrderHandler {

   // execution@Action

    	public booleanCustomer hideActact(CustomerPlaceOrderCommand customercommand) { ... }                  // supporting methods         // execution - infer the name of 
	the action from the type
}

and for the preconditions (no need for the "Act" suffix):

Code Block
public String disableAct(Customer customer) { ... }
	class CustomerPlaceOrderValidationHandler {

    public Stringboolean disable1Acthide(CustomerPlaceOrderCommand customercommand) { ... }
	        public Collection<Product> choices0Act(Customer customer) { ... }            // supporting methods
	    public ProductString default0Actdisable(CustomerPlaceOrderCommand customercommand) { ... }
	    public intString default1Actdisable1(CustomerPlaceOrderCommand customercommand) { ... }
	    public String validate1Actvalidate1(CustomerPlaceOrderCommand customercommand) { ... }
	    public String validateActvalidate(CustomerPlaceOrderCommand customercommand) { ... }
    }

   }

and for the UI hints:

Code Block
public class CustomerPlaceOrderUiHintsHandler { ...

    public staticCollection<Product> class PlaceOrderEvent extends ActionDomainEvent<Customer> {}                      choices0(PlaceOrderCommand command) { ... }              
  

  public Product @Actiondefault0(domainEvent = PlaceOrderEvent.classPlaceOrderCommand command) { ... }
    public int default1(PlaceOrderCommand command) { ... }
}

Command Handler Contracts

Command handlers in other frameworks often have a single method, called something like "apply" or "accept".  We can't quite get there because we not only need to execute the action, but also do the validation and UI hint stuff.

We could though introduce some API to define this contract.

Code Block
public class CustomerPlaceOrderHandler implements           // event publishingCommandActHandler<PlaceOrderCommand> {

    public Customer act(PlaceOrderParametersPlaceOrderCommand paramscommand) { return params.act(this);... }              
}

To hide entire action:

Code Block
public class CustomerPlaceOrderHideActHandler implements CommandHideActHandler<PlaceOrderCommand> {
            // remainder is just boilerplate

    public boolean hideAct(PlaceOrderParameters params) { return params.hide(this); }         public boolean hide(PlaceOrderCommand command) { ... }                          
}

To hide individual parameters:

Code Block
public class CustomerPlaceOrderHideParamHandler implements CommandHideParamHandler<PlaceOrderCommand> {
    public String disableAct(PlaceOrderParameters paramshide(PlaceOrderCommand command, int paramNum) { ... }
}

to disable entire action:

Code Block
public class CustomerPlaceOrderDisableActHandler implements CommandDisableActHandler<PlaceOrderCommand> {return params.disable(this); }
    public String disable1Act(PlaceOrderParameters params) { return params.disable1(this); }
    public Collection<Product>String choices0Actdisable(PlaceOrderParametersPlaceOrderCommand paramscommand) { return params.choices0(this); }     ... }
}

To disable individual parameters:

Code Block
public class CustomerPlaceOrderDisableHandler implements CommandDisableParamHandler<PlaceOrderCommand> {
    public String disable(PlaceOrderCommand command, int 
paramNum) { ... }
}

To validate entire parameter set:

Code Block
  public Productclass default0Act(PlaceOrderParameters params) { return params.default0(this); }CustomerPlaceOrderValidateActHandler implements CommandValidateActHandler<PlaceOrderCommand> {
    public intString default1Actvalidate(PlaceOrderParametersPlaceOrderCommand paramscommand) { return params.default1(this);... }
}

To validate individual parameters:

Code Block
public class CustomerPlaceOrderValidateParamHandler  public String validate1Act(PlaceOrderParameters params) { return params.validate1(this); }implements CommandValidateParamHandler<PlaceOrderCommand> {
    public String validateActvalidate(PlaceOrderParametersPlaceOrderCommand params)command, { params.validate(this); }
}

Notes:

  • the target is still outside of the parameters object
  • Event publishing also outside
  • Everything else has moved inside the parameters object
  • This implies that we would need dependency injection for the parameters object
  • The rest of the code in the mixin is just boilerplate.  It's possible that the Lombok @Delegate annotation might be used to remove some of this boilerplate, didn't investigate further.

Mixins and Parameters combined (proposed)

The previous section describes an idiom to work within the new Parameter object programming model.  But the next step along the journey would be to formally recognise this pattern.  This would amount to collapsing the mixin concept and the parameters concept into the same thing.  Said another way, mixins start to become stateful, keeping track of the parameter argument values as well as the target object:

int paramNum) { ... }
}

And we keep going for the UI hints.

To return choices:

Code Block
public class CustomerPlaceOrderChoicesParamHandler implements CommandChoicesParamHandler<PlaceOrderCommand> {
    public Collection<Object> choices(PlaceOrderCommand command, int paramNum) { ... }                              // bit ugly
}

To provide an autoComplete:

Code Block
public class CustomerPlaceOrderAutoCompleteHandler implements CommandAutoCompleteParamHandler<PlaceOrderCommand> {
    public Collection<Object> autoComplete(PlaceOrderCommand command, int paramNum, String search) { ... }
Code Block
@Action
public class Customer_placeOrder {

    private final Customer target;                            // bit ugly
}

To return defaults: 

Code Block
public class CustomerPlaceOrderDefaultParamHandler implements CommandDefaultParamHandler<PlaceOrderCommand> {
    public Object defaultOf(PlaceOrderCommand command, int   // target
   paramNum) { ...

 }   @Parameter() @MemberOrder(1)                                     // 'default' is a reserved word
}

Of course, there's nothing to prevent a single class from implementing all of these interfaces:

Code Block
@Action
public class CustomerPlaceOrderHandler 
   // supporting methods support
 implements CommandActHandler<PlaceOrderCommand>,
  Product product;
    @Parameter() @MemberOrder(2)
    int quantity;

    public static classCommandHideActHandler<PlaceOrderCommand>,
 PlaceOrderEvent extends ActionDomainEvent<Customer> {}              CommandHideParamHandler<PlaceOrderCommand>, 
                  CommandDisableActHandler<PlaceOrderCommand>, 
    
    @Action(domainEvent = PlaceOrderEvent.class)        CommandDisableParamHandler<PlaceOrderCommand>,
                  CommandValidateActHandler<PlaceOrderCommand>,
      // event publishing
    public Customer act() { ... } CommandValidateParamHandler<PlaceOrderCommand>,
                  CommandChoicesParamHandler<PlaceOrderCommand>,
                  CommandAutoCompleteParamHandler<PlaceOrderCommand>,
          // execution 

    public boolean hideAct()CommandDefaultParamHandler<PlaceOrderCommand> {

 ...  } public Customer act(PlaceOrderCommand command) { ... }   
    public boolean hide(PlaceOrderCommand command) { ...  }                       // supporting methods
    public String disableAct(hide(PlaceOrderCommand command, int paramNum) { ... }
    public String disable1Actdisable(PlaceOrderCommand command) { ... } {
    public Collection<Product>String choices0Act(disable(PlaceOrderCommand command, int paramNum) { ... }              
    public ProductString default0Actvalidate(PlaceOrderCommand command) { ... }
    public String validate(PlaceOrderCommand command, int default1Act(paramNum) { ... }
    public String validate1Act( Collection<Object> choices(PlaceOrderCommand command, int paramNum) { ... } 
    public StringCollection<Object> validateAct() { ... }
}

Notes:

  • here the supporting methods would simply read from the fields of the mixin that represent the parameters of the mixin itself.
  • the domain event class is still separate

Mixin and Parameters and Domain Event combined (proposed)

...

autoComplete(PlaceOrderCommand command, int paramNum, String search) { ... } 
    public Object defaultOf(PlaceOrderCommand command, int paramNum) { ... }
}