You are viewing a plain text version of this content. The canonical link for it is here.
Posted to users@isis.apache.org by Dan Haywood <da...@haywood-associates.co.uk> on 2014/07/01 00:20:17 UTC

Re: Ideas on how to use the EventBus for validation.

I've raised ISIS-831 [1] to summarize my ideas on this... any feedback
welcome.

Thx
Dan


https://issues.apache.org/jira/browse/ISIS-831


On 30 June 2014 10:04, Dan Haywood <da...@haywood-associates.co.uk> wrote:

> Hi Oscar,
> thanks for the quick response; within
>
> On 30 June 2014 08:00, GESCONSULTOR <o....@gesconsultor.com> wrote:
>
>> Se has a similar use cases to this one, when an event should be published
>> when the user wanted to remove the domain object.
>>
>> We can publish the event (eventBus.post(...)) on the "removing" lifecycle
>> method.
>>
>> As it's executed in the context of the same transaction where we change
>> the references in all other objects, DN hasn't dispatched the delete
>> command, no references to deleted objects are kept on the database.
>>
>> Shouldn't it be enough for this case? Perhaps I'm missing something.
>>
>>
>
> This approach won't work, because we need to replace the reference with a
> different one.  It's basically a 2cascade update" of the impacted objects.
>
> Dan
>
>
>
>
>
>
>>
>>

Re: Ideas on how to use the EventBus for validation.

Posted by Dan Haywood <da...@haywood-associates.co.uk>.
OK, this ticket (ISIS-831) has now been implemented and will be in 1.6.0.

Notable points:

- every interaction with action, property and collection generates events
onto the event bus.
- if the action/property/collection is annotated (using @ActionInteraction,
@PropertyInteraction, @CollectionInteraction) then the event type specified
will be posted.
- If there is no annotation, then an instance of
"XxxInteractionEvent.Default" is posted instead
- each event is sent to subscribers (up to) 5 times: hide, disable,
validate, executing and executed; the ev.getPhase() can be used to
distinguish
- the same event is passed from the validate -> executing phases, meaning
that the subscriber can use the event as a means to transfer state from one
phase to another.

~~~

The use case that started all this was a requirement in Estatio app [1] to
implement a cascade update when deleting a CommunicationChannel; any
references to that CC in the AgreementRoleCommunicationChannel entity must
be updated with a replacement CC.

This is now implemented as follows.  In the entity

public abstract class CommunicationChannel ... {
   ...

   public static class RemoveEvent extends
ActionInteractionEvent<CommunicationChannel> { ... }

  @ActionInteraction(CommunicationChannel.RemoveEvent.class)
    public void remove(@Named("Replace with") @Optional
CommunicationChannel replacement) {
        getContainer().remove(this);
    }
}

and in the subscriber:

@DomainService
public class AgreementRoleCommunicationChannels ... {

    ...
    @Subscribe
    public void on(final CommunicationChannel.RemoveEvent ev) {
        CommunicationChannel sourceCommunicationChannel =
(CommunicationChannel) ev.getSource();
        CommunicationChannel replacementCommunicationChannel =
ev.getReplacement();

        switch (ev.getPhase()) {
            case VALIDATE:
                final List<AgreementRoleCommunicationChannel>
communicationChannels =
findByCommunicationChannel(sourceCommunicationChannel);

                if (communicationChannels.size() > 0 &&
replacementCommunicationChannel == null) {
                    ev.invalidate("Communication channel is being used:
provide a replacement");
                }

                ev.setCommunicationChannels(communicationChannels);
                break;
            case EXECUTING:
                for (AgreementRoleCommunicationChannel arcc :
ev.getImpactedCommunicationChannels()) {

arcc.setCommunicationChannel(replacementCommunicationChannel);
                }
                break;
        }
    }
}

I'm pretty excited about this new functionality; reckon it opens up all
sorts of scenarios to help decouple business logic.  For example, one could
have a subscriber whose sole job is to apply cross-cutting business rules.
 That subscriber could even be named after the business rule being
implemented.  In the above example, we could have called our subscriber
class "ReplaceCommunicationChannelWhereverUsed" as a way to describe its
responsibility.  (Now that we have @DomainService to automatically register
these classes, there is no real downside to having such fine-grained
services).


Cheers
Dan



[1] http://github.com/estatio/estatio
[2]
https://github.com/estatio/estatio/blob/bd2b6b932d128def036231fc1d51b58b4fcd8c14/dom/src/main/java/org/estatio/dom/communicationchannel/CommunicationChannel.java#L194
[3]








On 8 July 2014 07:28, Dan Haywood <da...@haywood-associates.co.uk> wrote:

> Slight correction to previous post: the names I have chosen for the new
> annotations are
>
> @InteractionWithAction, @InteractionWithProperty,
> @InteractionWithCollection
>
> rather than
>
> @ActionInteraction, @PropertyInteraction, @CollectionInteraction.
>
> I'm really unsure which to use, keep changing my mind.  The
> "InteractionWith" prefix I like cos it groups these together lexically, and
> is more descriptive, but it's a bit clunky as a name; the second I like cos
> they are snappier.
>
> Opinions, if any?
>
> Dan
>
>
>
>
>
> On 8 July 2014 07:21, Dan Haywood <da...@haywood-associates.co.uk> wrote:
>
>> Have started implementing this story now (ISIS-831), looking for some
>> opinions.  Oscar will chip in I'm sure :-) but I'd like other opinions too,
>> if possible.
>>
>>
>> Right now I'm planning to have an @ActionInteraction,
>> @PropertyInteraction and @CollectionInteraction; these are basically
>> replacements for @PostsActionInvokedEvent, @PostsPropertyChangedEvent and
>> @PostsCollectionAddedToEvent/@PostsCollectionRemovedFromEvent, and support
>> the validation stuff.  The original 4 @PostsXxxEvent annotations will be
>> deprecated.
>>
>> Because they are intended as replacements, if both annotations are
>> present then I intend only to generate one event on the event bus.
>>
>> ~~~
>> You'll note that I've collapsed the two annotations for collections
>> (add/remove) into a single annotation for collections.  I think I prefer
>> this.
>>
>> This does raise the question of whether to preserve two event classes for
>> collections (eg a CollectionAddInteractionEvent and
>> CollectionRemoveInteractionEvent), or whether to collapse into a single
>> event and have a field on the event class so that the subscriber can tell
>> whether an add or remove was performed.
>>
>> ~~~
>> The first design looks results in more boilerplate for the publisher:
>>
>> public static class OrderLinesAdd extends CollectionAddInteractionEvent {
>> ... }
>> public static class OrderLinesRemove extends
>> CollectionRemoveInteractionEvent { ... }
>>
>> @CollectionInteraction(add=OrderLinesAdd.class,
>> remove=OrderLinesRemove.class)
>> public Set<OrderLine> getOrderLines() { ...}
>>
>> but is cleaner for the subscriber:
>>
>> @Subscribe
>> public void on(OrderLinesAdd ev) { ...}
>> @Subscribe
>> public void on(OrderLinesRemove ev) { ...}
>>
>> ~~~
>> The second design is simpler for the publisher:
>>
>> public static class OrderLines extends CollectionRemoveInteractionEvent {
>> ... }
>>
>> @CollectionInteraction(OrderLines.class)
>> public Set<OrderLine> getOrderLines() { ...}
>>
>> but results in more code for the subscriber:
>>
>> @Subscribe
>> public void on(OrderLines ev) {
>>     if (ev.getType == ADD_TO) {
>>       ... add logic ...
>>     } else {
>>        ... remove logic ...
>>     }
>>
>>
>> Of these, I prefer the second design myself - I'd rather keep boilerplate
>> in the publishing entities down to a minimum.  But other opinions welcome.
>>
>> ~~~
>> Also, I've named the annotations @ActionInteraction, @PropertyInteraction
>> and @CollectionInteraction, but it occurred to me that they could be
>> shortened to just @Action, @Property, @Collection.  This might be a bad
>> thing or a good thing:
>> - it might be a bad thing because it would suggest that the only methods
>> that are recognised as actions, properties and collections are those
>> annotated in this way; where as Isis recognises everything that isn't
>> @Ignore'd.
>> - it might be a good thing because we could provide a mode of running
>> Isis whereby all methods are ignored by default unless explicitly
>> annotated.  In that case these annotations would flag the bits of the class
>> that are significant to Isis.
>>
>> I think the bad outweighs the good, meaning I should keep the
>> "Interaction" suffix and keep the current behaviour of recognizing
>> everything unless @Ignore'd .... but thought it raising in case anyone has
>> any opinions on this matter.
>>
>> Thx
>> Dan
>>
>>
>>
>>
>>
>> On 3 July 2014 12:21, Dan Haywood <da...@haywood-associates.co.uk> wrote:
>>
>>>
>>>
>>> On 3 July 2014 12:15, GESCONSULTOR <o....@gesconsultor.com> wrote:
>>>
>>>>
>>>> Agree with you, Dan.
>>>>
>>>> Simply the @ActionInteraction perhaps should have the @PostsXXX prefix
>>>> for coherence.
>>>>
>>>>
>>> I thought about that, but I thought that "PostsXxx" implies an event
>>> being fired after (which used to be the case, of course) whereas in the new
>>> proposed design there are four events fired, three before the execute, one
>>> after.
>>>
>>> Another alternative I had thought of was to fold this into @Command, eg:
>>>
>>> @Command(...., actionInteraction=RemoveActionInteraction.class)
>>>
>>> One issue though is that commands only apply to actions, not to
>>> properties and collections (remember: there is the hide and disable events
>>> as part of this).  So perhaps probably best not too combine just yet, until
>>> things become clearer.
>>>
>>> Other suggestions welcome, of course!
>>>
>>>
>>> Ta
>>> Dan
>>>
>>>
>>>
>>>
>>>> Many thanks!!
>>>>
>>>>
>>>> > El 03/07/2014, a las 12:25, Dan Haywood <da...@haywood-associates.co.uk>
>>>> escribió:
>>>> >
>>>> > @ActionInteraction
>>>>
>>>
>>>
>>
>

Re: Ideas on how to use the EventBus for validation.

Posted by Dan Haywood <da...@haywood-associates.co.uk>.
Slight correction to previous post: the names I have chosen for the new
annotations are

@InteractionWithAction, @InteractionWithProperty, @InteractionWithCollection

rather than

@ActionInteraction, @PropertyInteraction, @CollectionInteraction.

I'm really unsure which to use, keep changing my mind.  The
"InteractionWith" prefix I like cos it groups these together lexically, and
is more descriptive, but it's a bit clunky as a name; the second I like cos
they are snappier.

Opinions, if any?

Dan





On 8 July 2014 07:21, Dan Haywood <da...@haywood-associates.co.uk> wrote:

> Have started implementing this story now (ISIS-831), looking for some
> opinions.  Oscar will chip in I'm sure :-) but I'd like other opinions too,
> if possible.
>
>
> Right now I'm planning to have an @ActionInteraction, @PropertyInteraction
> and @CollectionInteraction; these are basically replacements for
> @PostsActionInvokedEvent, @PostsPropertyChangedEvent and
> @PostsCollectionAddedToEvent/@PostsCollectionRemovedFromEvent, and support
> the validation stuff.  The original 4 @PostsXxxEvent annotations will be
> deprecated.
>
> Because they are intended as replacements, if both annotations are present
> then I intend only to generate one event on the event bus.
>
> ~~~
> You'll note that I've collapsed the two annotations for collections
> (add/remove) into a single annotation for collections.  I think I prefer
> this.
>
> This does raise the question of whether to preserve two event classes for
> collections (eg a CollectionAddInteractionEvent and
> CollectionRemoveInteractionEvent), or whether to collapse into a single
> event and have a field on the event class so that the subscriber can tell
> whether an add or remove was performed.
>
> ~~~
> The first design looks results in more boilerplate for the publisher:
>
> public static class OrderLinesAdd extends CollectionAddInteractionEvent {
> ... }
> public static class OrderLinesRemove extends
> CollectionRemoveInteractionEvent { ... }
>
> @CollectionInteraction(add=OrderLinesAdd.class,
> remove=OrderLinesRemove.class)
> public Set<OrderLine> getOrderLines() { ...}
>
> but is cleaner for the subscriber:
>
> @Subscribe
> public void on(OrderLinesAdd ev) { ...}
> @Subscribe
> public void on(OrderLinesRemove ev) { ...}
>
> ~~~
> The second design is simpler for the publisher:
>
> public static class OrderLines extends CollectionRemoveInteractionEvent {
> ... }
>
> @CollectionInteraction(OrderLines.class)
> public Set<OrderLine> getOrderLines() { ...}
>
> but results in more code for the subscriber:
>
> @Subscribe
> public void on(OrderLines ev) {
>     if (ev.getType == ADD_TO) {
>       ... add logic ...
>     } else {
>        ... remove logic ...
>     }
>
>
> Of these, I prefer the second design myself - I'd rather keep boilerplate
> in the publishing entities down to a minimum.  But other opinions welcome.
>
> ~~~
> Also, I've named the annotations @ActionInteraction, @PropertyInteraction
> and @CollectionInteraction, but it occurred to me that they could be
> shortened to just @Action, @Property, @Collection.  This might be a bad
> thing or a good thing:
> - it might be a bad thing because it would suggest that the only methods
> that are recognised as actions, properties and collections are those
> annotated in this way; where as Isis recognises everything that isn't
> @Ignore'd.
> - it might be a good thing because we could provide a mode of running Isis
> whereby all methods are ignored by default unless explicitly annotated.  In
> that case these annotations would flag the bits of the class that are
> significant to Isis.
>
> I think the bad outweighs the good, meaning I should keep the
> "Interaction" suffix and keep the current behaviour of recognizing
> everything unless @Ignore'd .... but thought it raising in case anyone has
> any opinions on this matter.
>
> Thx
> Dan
>
>
>
>
>
> On 3 July 2014 12:21, Dan Haywood <da...@haywood-associates.co.uk> wrote:
>
>>
>>
>> On 3 July 2014 12:15, GESCONSULTOR <o....@gesconsultor.com> wrote:
>>
>>>
>>> Agree with you, Dan.
>>>
>>> Simply the @ActionInteraction perhaps should have the @PostsXXX prefix
>>> for coherence.
>>>
>>>
>> I thought about that, but I thought that "PostsXxx" implies an event
>> being fired after (which used to be the case, of course) whereas in the new
>> proposed design there are four events fired, three before the execute, one
>> after.
>>
>> Another alternative I had thought of was to fold this into @Command, eg:
>>
>> @Command(...., actionInteraction=RemoveActionInteraction.class)
>>
>> One issue though is that commands only apply to actions, not to
>> properties and collections (remember: there is the hide and disable events
>> as part of this).  So perhaps probably best not too combine just yet, until
>> things become clearer.
>>
>> Other suggestions welcome, of course!
>>
>>
>> Ta
>> Dan
>>
>>
>>
>>
>>> Many thanks!!
>>>
>>>
>>> > El 03/07/2014, a las 12:25, Dan Haywood <da...@haywood-associates.co.uk>
>>> escribió:
>>> >
>>> > @ActionInteraction
>>>
>>
>>
>

Re: Ideas on how to use the EventBus for validation.

Posted by Dan Haywood <da...@haywood-associates.co.uk>.
Have started implementing this story now (ISIS-831), looking for some
opinions.  Oscar will chip in I'm sure :-) but I'd like other opinions too,
if possible.


Right now I'm planning to have an @ActionInteraction, @PropertyInteraction
and @CollectionInteraction; these are basically replacements for
@PostsActionInvokedEvent, @PostsPropertyChangedEvent and
@PostsCollectionAddedToEvent/@PostsCollectionRemovedFromEvent, and support
the validation stuff.  The original 4 @PostsXxxEvent annotations will be
deprecated.

Because they are intended as replacements, if both annotations are present
then I intend only to generate one event on the event bus.

~~~
You'll note that I've collapsed the two annotations for collections
(add/remove) into a single annotation for collections.  I think I prefer
this.

This does raise the question of whether to preserve two event classes for
collections (eg a CollectionAddInteractionEvent and
CollectionRemoveInteractionEvent), or whether to collapse into a single
event and have a field on the event class so that the subscriber can tell
whether an add or remove was performed.

~~~
The first design looks results in more boilerplate for the publisher:

public static class OrderLinesAdd extends CollectionAddInteractionEvent {
... }
public static class OrderLinesRemove extends
CollectionRemoveInteractionEvent { ... }

@CollectionInteraction(add=OrderLinesAdd.class,
remove=OrderLinesRemove.class)
public Set<OrderLine> getOrderLines() { ...}

but is cleaner for the subscriber:

@Subscribe
public void on(OrderLinesAdd ev) { ...}
@Subscribe
public void on(OrderLinesRemove ev) { ...}

~~~
The second design is simpler for the publisher:

public static class OrderLines extends CollectionRemoveInteractionEvent {
... }

@CollectionInteraction(OrderLines.class)
public Set<OrderLine> getOrderLines() { ...}

but results in more code for the subscriber:

@Subscribe
public void on(OrderLines ev) {
    if (ev.getType == ADD_TO) {
      ... add logic ...
    } else {
       ... remove logic ...
    }


Of these, I prefer the second design myself - I'd rather keep boilerplate
in the publishing entities down to a minimum.  But other opinions welcome.

~~~
Also, I've named the annotations @ActionInteraction, @PropertyInteraction
and @CollectionInteraction, but it occurred to me that they could be
shortened to just @Action, @Property, @Collection.  This might be a bad
thing or a good thing:
- it might be a bad thing because it would suggest that the only methods
that are recognised as actions, properties and collections are those
annotated in this way; where as Isis recognises everything that isn't
@Ignore'd.
- it might be a good thing because we could provide a mode of running Isis
whereby all methods are ignored by default unless explicitly annotated.  In
that case these annotations would flag the bits of the class that are
significant to Isis.

I think the bad outweighs the good, meaning I should keep the "Interaction"
suffix and keep the current behaviour of recognizing everything unless
@Ignore'd .... but thought it raising in case anyone has any opinions on
this matter.

Thx
Dan





On 3 July 2014 12:21, Dan Haywood <da...@haywood-associates.co.uk> wrote:

>
>
> On 3 July 2014 12:15, GESCONSULTOR <o....@gesconsultor.com> wrote:
>
>>
>> Agree with you, Dan.
>>
>> Simply the @ActionInteraction perhaps should have the @PostsXXX prefix
>> for coherence.
>>
>>
> I thought about that, but I thought that "PostsXxx" implies an event being
> fired after (which used to be the case, of course) whereas in the new
> proposed design there are four events fired, three before the execute, one
> after.
>
> Another alternative I had thought of was to fold this into @Command, eg:
>
> @Command(...., actionInteraction=RemoveActionInteraction.class)
>
> One issue though is that commands only apply to actions, not to properties
> and collections (remember: there is the hide and disable events as part of
> this).  So perhaps probably best not too combine just yet, until things
> become clearer.
>
> Other suggestions welcome, of course!
>
>
> Ta
> Dan
>
>
>
>
>> Many thanks!!
>>
>>
>> > El 03/07/2014, a las 12:25, Dan Haywood <da...@haywood-associates.co.uk>
>> escribió:
>> >
>> > @ActionInteraction
>>
>
>

Re: Ideas on how to use the EventBus for validation.

Posted by Dan Haywood <da...@haywood-associates.co.uk>.
On 3 July 2014 12:15, GESCONSULTOR <o....@gesconsultor.com> wrote:

>
> Agree with you, Dan.
>
> Simply the @ActionInteraction perhaps should have the @PostsXXX prefix for
> coherence.
>
>
I thought about that, but I thought that "PostsXxx" implies an event being
fired after (which used to be the case, of course) whereas in the new
proposed design there are four events fired, three before the execute, one
after.

Another alternative I had thought of was to fold this into @Command, eg:

@Command(...., actionInteraction=RemoveActionInteraction.class)

One issue though is that commands only apply to actions, not to properties
and collections (remember: there is the hide and disable events as part of
this).  So perhaps probably best not too combine just yet, until things
become clearer.

Other suggestions welcome, of course!


Ta
Dan




> Many thanks!!
>
>
> > El 03/07/2014, a las 12:25, Dan Haywood <da...@haywood-associates.co.uk>
> escribió:
> >
> > @ActionInteraction
>

Re: Ideas on how to use the EventBus for validation.

Posted by GESCONSULTOR <o....@gesconsultor.com>.
Agree with you, Dan.

Simply the @ActionInteraction perhaps should have the @PostsXXX prefix for coherence.

Many thanks!!


> El 03/07/2014, a las 12:25, Dan Haywood <da...@haywood-associates.co.uk> escribió:
> 
> @ActionInteraction

Re: Ideas on how to use the EventBus for validation.

Posted by Dan Haywood <da...@haywood-associates.co.uk>.
Thanks for picking this up, Oscar... within.


On 2 July 2014 08:34, GESCONSULTOR - Óscar Bou <o....@gesconsultor.com>
wrote:

>
>
> I think that there are 2 different (but related) things in your post.
>
> 1. When to post an "deleting object" event.
> 2. How to control (veto) an action by subscriptions.
>
>
But (1) is a bit more nuanced than that; it's about a cascading update of
impacted objects, rather than a simple cascade delete.  And because of the
cascade update, that's why there could be a veto (if no replacement is
provided).



>
> FIRST USE CASE
>
> On the first case, let me explain our use case.
>
> ...
> we want to be notified when the Relationship is deleted in order to
> automatically delete the RelationshipRiskInformation.
>
>
>
Understood... but that's a cascade delete, not a cascade update; so it's a
bit simpler.




> In your case, it seems to me that, instead, you could change the
> references to the replacement entity if you publish the event by means of
> "eventBusService.post(event)" in the CODE of the action, right before
> proceeding with "container().remove(domainObject)" code.
>
>
I could do publish an event imperatively, it's true, but I'd like to make
it declarative if possible.



> SECOND USE CASE
>
> In your proposal, only one annotation
>
>  @PostsActionInvokedEvent
>
> Is defined, but I think the intention and behavior could be different for
> each business rule (disabling, validation, execution).
>
>
Hmm, could argue this either way.  There's only really one interaction from
the user's perspective, but that gives rise to these four "sub-events"
(hide/disable/validate/execute).

My preference is to generalize @PostsActionInvokedEvent (which currently is
named after just the "execute" bit) to encompass the overall interaction.
 I think you are arguing for keeping the same level of granularity, but
having new events for the business rules.




>
> - VETOING AN ACTION INVOKATION (VALIDATE)
>
> For the next part, what seems to me is that what you want, instead of
> explicitly invoke "eventBusService.post(...)", an event that can be
> automatically published BEFORE action invocation, in order to execute some
> code on an Event Handler and, also, including the possibility to "veto" the
> action.
>
> I would propose something like
>
>    @PostsActionValidatingEvent(CommunicationChannel.ValidatingEvent.class)
>
>
> In that case, I think that all event handlers subscribed to that event,
> should act as a means of "Chain of Responsiblity" pattern, where any of
> them could "veto" the action invokation with a message, for example.
>
>
Yes, irrespective of what the API is, this is certainly the required
behaviour: all subscribers are fired any one of them could veto.




> CommunicationChannel.ValidatingEvent.class should be a descendant of a
> DomainEvent subclass where Isis could, for example, pass the action and
> invoked params in a Map, and also a "token" field that indicates if the
> action invocation must be vetoed or not (with a "VALIDATION" message) -
> i.e., if at least one of the event handlers marks it as invalid it
> shouldn't execute it.
>
>
> - EXECUTING AN ACTION
>
> For the next part, what seems to me is that what you want, instead of
> explicitly invoke "eventBusService.post(...)", an event that can be
> automatically published BEFORE action invocation, in order to execute some
> code on an Event Handler and, also, including the possibility to "veto" the
> action.
>
> I would propose in fact 2 different events:
>
>    @PostsActionInvokingEvent(CommunicationChannel.RemovingEvent.class)
>
> Whose event is posted BEFORE effectively executing the action.
>
>
Not sure that I follow this... seems to be a type-safe equivalent of the
validation event you described above.

The validate event is basically our pre-condition check; I don't think we
require two.




>
> In that case, all event handlers would be invoked, but any of them could
> "veto" the execution.
> If an exception is thrown, the EvenBusService will act as currently
> implemented (aborting if it's a descendant of an Isis exception, ignoring
> it if not).
>
> And
>
>    @PostsActionInvokedEvent(CommunicationChannel.RemovedEvent.class)
>
> whose event is posted AFTER the action has been SUCCESSFULLY invoked.
>
> In that case, all event handlers would be invoked, but any of them could
> "veto" the execution because it has already been executed.
>
>
Remark: this is basically the behaviour we currently have.




>
>
> - DISABLING AN ACTION (DISABLE)
>
> I think that this case is different, as the Viewer must know if the action
> is enabled when drawing the UI (and also in other cases, i.e., BDD tests)
> and, for that, action shouldn't be executed (which could change the "State"
> of the Domain).
>
> So a different kind of base event would be needed, like
>
> @PostsActionDisablingEvent(...)
>
>
> And all event handlers, like on the validation case, acting as a means of
> "Chain of Responsiblity", could mark the action as "Disabled".
>
>

Although it's true that disabling (and hiding for that matter) is checked
when the UI is first rendered, I take the view that it is still part of the
same "interaction".  That is, the user's intent is to invoke an action...
for that they must be able to (a) see it, it (b) mustn't be disabled, they
(c) then must provide valid arguments, then (d) it is executed.  There
might be a gap of a minute or two between (b) and (c) but logically it's
part of the same thing.


~~~
I think what concerns me about the above code sketch is the amount of
boilerplate it would generate, plus very similar sounding annotations.
 Including also the hide event (which you omitted), for a given "remove"
action we would have:

public class CommunicationChannel {

    public static RemoveHidingEvent extends o.a.i.applib.events.HidingEvent
{ ... }
    public static RemoveDisablingEvent extends
o.a.i.applib.events.DisablingEvent { ... }
    public static RemoveValidatingEvent extends
o.a.i.applib.events.ValidatingEvent { ... }
    public static RemoveExecutedEvent extends
o.a.i.applib.events.ActionInvokedEvent { ... }

   public @PostsActionHidingEvent(RemoveHidingEvent.class)
   public @PostsActionDisablingEvent(RemoveDisabligEvent.class)
   public @PostsActionValidatingEvent(RemoveValidatingEvent.class)
   public @PostsActionInvokedEvent(RemoveExecutedEvent.class)
   public void remove(CommunicationChannel replacement) {
      ...
   }
}

which is just horrible!

My proposal is to have a single event class that is used in multiple
"modes" across each of these subevents:

public class CommunicationChannel {

    public static RemoveActionInteraction extends
o.a.i.applib.events.ActionInteraction { ... }

   public @ActionInteraction(RemoveActionInteraction.class)
   public void remove(CommunicationChannel replacement) {
      ...
   }
}

That I think is more manageable.  I've renamed the annotation
@PostsActionInvokedEvent to just @ActionInteraction, using "interaction" as
the more general name; ditto for the event superclass.





>
>
> GENERAL COMMENT
>
>
> In all previous cases, there's a requirement to explicitly annotate the
> action with the corresponding @PostsXXX annotation.
>
> Perhaps Isis could provide a "generic" mechanism to subscribe to all
> action invocations, domain object lifecycle events, etc. that wouldn't
> require to explicitly annotate them.
> That way would be possible to intercept and "change" the behavior of a
> given Domain Module developed by Isis, simply by intercepting those
> platform-level events.
>
>
This, I think, is a really good idea.

In my code sketch, it means that ActionInteraction is not abstract and is
the default class that is published; the developer can optionally choose to
publish a type-safe one otherwise.

If we implemented this then we could, perhaps, refactor the AuditingService
and the PublishingService to use this capability; it's all the same sort of
thing.  There's probably a tie-up with the CommandService too; a Command is
basically a reified "interaction".  One step at a time, though.


Thx for helping me work through this, Oscar.

Dan





>
> HTH,
>
> Oscar
>
>
>
>
>
>
>
>
>
>

Re: Ideas on how to use the EventBus for validation.

Posted by GESCONSULTOR - Óscar Bou <o....@gesconsultor.com>.
Hi Dan.

Not sure why you say ...
>> 
>> This approach won't work, because we need to replace the reference with a
>> different one.  It's basically a 2cascade update" of the impacted objects.


I think that there are 2 different (but related) things in your post.

1. When to post an "deleting object" event.
2. How to control (veto) an action by subscriptions.


FIRST USE CASE

On the first case, let me explain our use case. 

We have one Entity (Relationship) with another one "extending" it with some other properties (RelationshipRiskInformation) in another Bounded Context (Maven module). 
We cannot implement referencial integrity as from the Maven module where the Relationship entity is defined, there's no dependency to the Risk module (in order to avoid circular relationships).

So we want to be notified when the Relationship is deleted in order to automatically delete the RelationshipRiskInformation.

In that case, we have published the event on the "persisting" Lifecycle method, (i.e., that should be invoked before the DN command be enqueued):

An, when we intercept it, we properly delete the related RelationshipBCMInformation.

In your case, it seems to me that, instead, you could change the references to the replacement entity if you publish the event by means of "eventBusService.post(event)" in the CODE of the action, right before proceeding with "container().remove(domainObject)" code.


SECOND USE CASE

In your proposal, only one annotation

 @PostsActionInvokedEvent

Is defined, but I think the intention and behavior could be different for each business rule (disabling, validation, execution).


- VETOING AN ACTION INVOKATION (VALIDATE)

For the next part, what seems to me is that what you want, instead of explicitly invoke "eventBusService.post(...)", an event that can be automatically published BEFORE action invocation, in order to execute some code on an Event Handler and, also, including the possibility to "veto" the action.

I would propose something like 

   @PostsActionValidatingEvent(CommunicationChannel.ValidatingEvent.class)


In that case, I think that all event handlers subscribed to that event, should act as a means of "Chain of Responsiblity" pattern, where any of them could "veto" the action invokation with a message, for example.

CommunicationChannel.ValidatingEvent.class should be a descendant of a DomainEvent subclass where Isis could, for example, pass the action and invoked params in a Map, and also a "token" field that indicates if the action invocation must be vetoed or not (with a "VALIDATION" message) - i.e., if at least one of the event handlers marks it as invalid it shouldn't execute it.


- EXECUTING AN ACTION

For the next part, what seems to me is that what you want, instead of explicitly invoke "eventBusService.post(...)", an event that can be automatically published BEFORE action invocation, in order to execute some code on an Event Handler and, also, including the possibility to "veto" the action.

I would propose in fact 2 different events:

   @PostsActionInvokingEvent(CommunicationChannel.RemovingEvent.class)

Whose event is posted BEFORE effectively executing the action.


In that case, all event handlers would be invoked, but any of them could "veto" the execution. 
If an exception is thrown, the EvenBusService will act as currently implemented (aborting if it's a descendant of an Isis exception, ignoring it if not).

And 

   @PostsActionInvokedEvent(CommunicationChannel.RemovedEvent.class)

whose event is posted AFTER the action has been SUCCESSFULLY invoked.

In that case, all event handlers would be invoked, but any of them could "veto" the execution because it has already been executed. 



- DISABLING AN ACTION (DISABLE)

I think that this case is different, as the Viewer must know if the action is enabled when drawing the UI (and also in other cases, i.e., BDD tests) and, for that, action shouldn't be executed (which could change the "State" of the Domain).

So a different kind of base event would be needed, like

@PostsActionDisablingEvent(...)


And all event handlers, like on the validation case, acting as a means of "Chain of Responsiblity", could mark the action as "Disabled".



GENERAL COMMENT


In all previous cases, there's a requirement to explicitly annotate the action with the corresponding @PostsXXX annotation.

Perhaps Isis could provide a "generic" mechanism to subscribe to all action invocations, domain object lifecycle events, etc. that wouldn't require to explicitly annotate them.
That way would be possible to intercept and "change" the behavior of a given Domain Module developed by Isis, simply by intercepting those platform-level events. 


HTH,

Oscar












El 01/07/2014, a las 00:20, Dan Haywood <da...@haywood-associates.co.uk> escribió:

> I've raised ISIS-831 [1] to summarize my ideas on this... any feedback
> welcome.
> 
> Thx
> Dan
> 
> 
> https://issues.apache.org/jira/browse/ISIS-831
> 
> 
> On 30 June 2014 10:04, Dan Haywood <da...@haywood-associates.co.uk> wrote:
> 
>> Hi Oscar,
>> thanks for the quick response; within
>> 
>> On 30 June 2014 08:00, GESCONSULTOR <o....@gesconsultor.com> wrote:
>> 
>>> Se has a similar use cases to this one, when an event should be published
>>> when the user wanted to remove the domain object.
>>> 
>>> We can publish the event (eventBus.post(...)) on the "removing" lifecycle
>>> method.
>>> 
>>> As it's executed in the context of the same transaction where we change
>>> the references in all other objects, DN hasn't dispatched the delete
>>> command, no references to deleted objects are kept on the database.
>>> 
>>> Shouldn't it be enough for this case? Perhaps I'm missing something.
>>> 
>>> 
>> 
>> This approach won't work, because we need to replace the reference with a
>> different one.  It's basically a 2cascade update" of the impacted objects.
>> 
>> Dan
>> 
>> 
>> 
>> 
>> 
>> 
>>> 
>>> 


Óscar Bou Bou
Responsable de Producto
Auditor Jefe de Certificación ISO 27001 en BSI
CISA, CRISC, APMG ISO 20000, ITIL-F

   902 900 231 / 620 267 520
   http://www.twitter.com/oscarbou

   http://es.linkedin.com/in/oscarbou

   http://www.GesConsultor.com 




Este mensaje y los ficheros anexos son confidenciales. Los mismos contienen información reservada que no puede ser difundida. Si usted ha recibido este correo por error, tenga la amabilidad de eliminarlo de su sistema y avisar al remitente mediante reenvío a su dirección electrónica; no deberá copiar el mensaje ni divulgar su contenido a ninguna persona.
Su dirección de correo electrónico junto a sus datos personales constan en un fichero titularidad de Gesdatos Software, S.L. cuya finalidad es la de mantener el contacto con Ud. Si quiere saber de qué información disponemos de Ud., modificarla, y en su caso, cancelarla, puede hacerlo enviando un escrito al efecto, acompañado de una fotocopia de su D.N.I. a la siguiente dirección: Gesdatos Software, S.L. , Paseo de la Castellana, 153 bajo - 28046 (Madrid), y Avda. Cortes Valencianas num. 50, 1ºC - 46015 (Valencia). Asimismo, es su responsabilidad comprobar que este mensaje o sus archivos adjuntos no contengan virus informáticos, y en caso que los tuvieran eliminarlos.