Thanks for picking this up, Oscar... within.

On 2 July 2014 08:34, GESCONSULTOR - Óscar Bou <[email protected]>
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
>
>
>
>
>
>
>
>
>
>

Reply via email to