Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: removed the publishedEvent stuff... reintroduced @MemberOrder just for last syntax, as is necessary I think

...

  • For disable1PlaceOrder(), we could also hide individual parameters similarly
  • Instead of choices0PlaceOrder(), we could have used autoComplete0PlaceOrder(String)

UPDATE: we'll ignore the event publishing part ... in an earlier version of this paper we didn't see any value in trying to unify it with the other responsibilities.

Standard syntax

The standard syntax uses regular methods on the target object.  Naming conventions are used to associate the action with supporting methods (default, choices, hide, disable and validate).

...

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() { ... }
    public String validate1PlaceOrder(int quantity) { ... }
    public String validatePlaceOrder(Product p, int quantity) { ... }
}


Mixins syntax

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

So the real target is simply:

Code Block
public class Customer {}


and the action itself moves onto the mixin:

Code Block
@Action         // supporting methods
    public String disablePlaceOrder() { ... }
    public String disable1PlaceOrder(Product p) { ... }
             public Collection<Product> choices0PlaceOrder() { ... }
    public Product default0PlaceOrder() { ... }
    
public intclass default1PlaceOrderCustomer_placeOrder() { ... }
        public String validate1PlaceOrder(int quantity) { ... }
    public String validatePlaceOrder(Product p, int quantity) { ... }
}

...

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

So the real target is simply:

Code Block
public class Customer {}

and the action itself moves onto the mixin:

Code Block
@Action

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

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

    privatepublic finalboolean Customer target;  hideAct() { ... }                                              // targetsupporting methods
    public String Customer_placeOrder(Customer targetdisableAct() { ... }

    public staticString class PlaceOrderEvent extends ActionDomainEvent<Customer> {}disable1Act(Product p) { ... }
    public Collection<Product> choices0Act() { ... }
    public Product default0Act() { ... }
    public int default1Act() { ...}
    public        

    @Action(domainEvent = PlaceOrderEvent.class)                                  // event publishingString validate1Act(int quantity) { ... }
    public CustomerString actvalidateAct(Product p, int quantity) { ... }                          // action execution

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

Notes:

  • 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.

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> {}                                      

    @Action(domainEvent = PlaceOrderEvent.class)                                  // event publishing
    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 static class PlaceOrderEvent extends ActionDomainEvent<Customer> {}                                      

    @Action(domainEvent = PlaceOrderEvent.class)                                  // event publishing
    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:


}

Notes:

  • 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.

Code Block
languagejava
public class Customer {                                                           // target

    @Value @Accessors(fluent = true)                   
    public class PlaceOrderParameters {
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;
    }

    public static class PlacerOrderEvent extends ActionDomainEvent<Customer> {}                                      

    @Action(domainEvent = PlaceOrderEvent.class)     // to assist supporting methods 
        Product product;
        int quantity;
    }

  // event publishing@Action
    public Customer placeOrder(PlaceOrderParameters paramsProduct 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:

(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 this removes the duplication between the placeOrder(...) parameter list and the list of members in PlaceOrderParameters  method, as well as in the PlacerOrdersParameters 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 becomesThe above would also be supported with mixins:

Code Block
@Action
public class Customer_placeOrder {

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

    @Valueprivate @Accessors(fluentfinal = true)        Customer target;     
    public static class PlaceOrderParameters {                                    // to assist supporting methods
        @Parameter()
        Product product;
        @Parameter()
   target
    public int quantity;
    Customer_placeOrder(Customer target) { ... }

    public@Value static@Accessors(fluent class= PlaceOrderEventtrue) extends ActionDomainEvent<Customer> {}          
    public static class PlaceOrderParameters {                    

    @Action(domainEvent = PlaceOrderEvent.class)          // to assist supporting methods
        Product product;
        int quantity;
  // event publishing}

    public Customer act(PlaceOrderParameters paramsProduct 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 still now have three classes here (: the mixin, parameters and the domain event), but we have removed the duplication between the act(...) parameter list and the list of members of PlaceOrderParameters class

...

  • and the parameters object.

Parameters on Act 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 becomeThis is a variant of the previous, but uses the parameters class in the action as well:

Code Block
@Action
public class Customer_placeOrder {

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

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

    @Action
    public staticCustomer class PlaceOrderEvent extends ActionDomainEvent<Customer> {placeOrder(PlaceOrderParameters params) { ... }               // execution    

    public boolean hidePlaceOrder() { ... }          

    @Action(domainEvent = PlaceOrderEvent.class)                       // supporting methods use PlaceOrderParameters
    public String disablePlaceOrder() // event publishing{ ... }
    public CustomerString actdisable1PlaceOrder(PlaceOrderParameters params) { ... }
    public Collection<Product> choices0PlaceOrder() { ... }             // execution

    public Product default0PlaceOrder() { ... }
    public booleanint hideActdefault1PlaceOrder(PlaceOrderParameters params) { ... }
    public String validate1PlaceOrder(PlaceOrderParameters params) { ... }
    public  String validatePlaceOrder(PlaceOrderParameters params) // supporting methods
    public String disableAct(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;               . }
    public String disable1Act(PlaceOrderParameters params) { ... }
    public Collection<Product> choices0Act(PlaceOrderParameters params) { ... }              
    public Product default0Act(PlaceOrderParameters params) { ... }
    public int default1Act(PlaceOrderParameters params) { ... }// target
    public String validate1Act(PlaceOrderParameters paramsCustomer_placeOrder(Customer target) { ... }

    public@Value 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 {
@Accessors(fluent = true)             
    privatepublic static finalclass CustomerPlaceOrderParameters target;{                                    // to assist supporting methods
        @Parameter()
    public Customer_placeOrder(Customer target) { ... }

Product product;
     @Value @Accessors(fluent = true@Parameter()
        int quantity;
    }

    @Action
    public staticCustomer class PlaceOrderParametersact(PlaceOrderParameters params) { ... }                             // execution

    public boolean hideAct()   // see below{ ... }                                  

    public static class PlaceOrderEvent extends ActionDomainEvent<Customer> {}     // supporting methods
    public String disableAct() { ... }
    public String disable1Act(PlaceOrderParameters params) { ... }
    public Collection<Product> choices0Act() { ... }   

    @Action(domainEvent = PlaceOrderEvent.class)     
    public Product default0Act() { ... }
    public int default1Act() { ... }
    public String validate1Act(PlaceOrderParameters params) { ...  }
    public CustomerString actvalidateAct(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 {

    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 default0Act(PlaceOrderParameters paramsCustomer_placeOrder(Customer target) { return params.default0(this);... }

    public@Value int default1Act(PlaceOrderParameters params@Accessors(fluent = true) { return params.default1(this); }
    public String validate1Act(PlaceOrderParameters params) { return params.validate1(this); }
    public Stringstatic class validateAct(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()
        //Product targetproduct;
    ...

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

    @Action
    public staticCustomer class PlaceOrderParametersact(PlaceOrderParameters params) { ... }                      // execution

    public boolean hideAct(PlaceOrderParameters params) { ... }      

        @Parameter()
     // supporting methods
 Product product;
  public String disableAct(PlaceOrderParameters params) {  @Parameter()... }
    public String disable1Act(PlaceOrderParameters  int quantity;

	params) { ... }
    public CustomerCollection<Product> actchoices0Act(CustomerPlaceOrderParameters customerparams) { ... }              
    public Product default0Act(PlaceOrderParameters params) { // execution
... }
    	public booleanint hideActdefault1Act(CustomerPlaceOrderParameters customerparams) { ... }
    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 {

// supporting methods         private final Customer target;    
	    public String disableAct(Customer customer) { ... }
	         public String disable1Act(Customer customer) { ... }
	    public Collection<Product> choices0Act(Customer customer) { ... }              
	    public Product default0ActCustomer_placeOrder(Customer customertarget) { ... }
	
    public@Value int default1Act(Customer customer@Accessors(fluent = true) { ... }
	    public String validate1Act(Customer customer) { ... }
	    public Stringstatic validateAct(Customer customer)class PlaceOrderParameters { ... }
    }

    ...

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

      @Action(domainEvent = PlaceOrderEvent.class)            

    public static class PlaceOrderEvent extends ActionDomainEvent<Customer> {}     // event publishing
             public Customer act(PlaceOrderParameters params) { return params.act(this); }             

    @Action
    public Customer act(PlaceOrderParameters params) //{ remainder is just boilerplatereturn 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); }              
    public Product default0Act(PlaceOrderParameters params) { return params.default0(this); }
    public int default1Act(PlaceOrderParameters params) { return params.default1(this); }
    public String validate1Act(PlaceOrderParameters params) { return params.validate1(this); }
    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:

which would then beef up the parameters object:

Code Block
@Action
public class Customer_placeOrder {

    private final Customer target;                                         // target
    ...

    @Value @Accessors(fluent = true)             
    public static class PlaceOrderParameters {
Code Block
@Action
public class Customer_placeOrder {

    private final Customer target;                                              // target

    ...

    @Parameter()
        Product product;
        @Parameter()
        int quantity;

	    public Customer act(Customer customer) { ... }                     // execution

    	public boolean hideAct(Customer customer) { ... }     // supporting methods support
    Product product;
    @Parameter()
 // supporting methods int quantity;

    public static class PlaceOrderEvent extends ActionDomainEvent<Customer> {}    
	    public String disableAct(Customer customer) { ... }
	    public String disable1Act(Customer customer) { ... }
	    public Collection<Product> choices0Act(Customer customer) { ... }     
    @Action(domainEvent = PlaceOrderEvent.class)   
	    public Product default0Act(Customer customer) { ... }
	    public int default1Act(Customer customer) { ... }
	    public String validate1Act(Customer customer) { // event publishing
... }
	    public String validateAct(Customer act(customer) { ... }
    }

    ...

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

    public boolean hideAct(PlaceOrderParameters params) { return ...params.hide(this); }                                            // supporting methods
    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); }              
    public Product default0Act(PlaceOrderParameters params) { return ...params.default0(this); }
    public int default1Act(PlaceOrderParameters params) { ...return params.default1(this); }
    public String validate1Act(PlaceOrderParameters params) { ...return params.validate1(this); }
    public String validateAct(PlaceOrderParameters params) { ...params.validate(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

Mixin and Parameters and Domain Event combined (proposed - but a step too far?)

A further possible unification is to make the mixin also act as the domain event.  I am not sure this is a good idea... my mental model is that the mixin acts as the "command", representing an intention to perform something, where as the event is a record of a fact that occurred.  But maybe this is too theoretical, and actually it would make sense to combine the,  If so, it would look something like:

  • 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
Code Block
languagejava
@Action
public class Customer_placeOrder extends{

  ActionDomainEvent<Customer> { private final Customer target;        // ??? this looks very odd

    private final Customer target;                          // target
    ...

    @Parameter() @MemberOrder(1)                                                // supporting methods support
    Product product;
    @Parameter() @MemberOrder(2)
    int quantity;

    @Action(domainEvent = Customer_placeOrder.class)                              // ??? I wonder if this is valid syntactically
    public Customer act() { ... }                           // execution             ??? these methods would be available to// theexecution subscriber

    public boolean hideAct() { ... }                              //   supporting methods    ??? these methods would be available to// thesupporting subscribermethods
    public String disableAct() { ... }
    public String disable1Act() { ... }
    public Collection<Product> choices0Act() { ... }              
    public Product default0Act() { ... }
    public int default1Act() { ... }
    public String validate1Act() { ... }
    public String 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
  • @MemberOrder is required because the JVM does not guarantee the order in the bytecode is the same as in the source file.