Thanks for this reply, Oscar. It's interesting to me that you've used CQRS for the more technical (sub)domains of your system, rather than the more business-oriented stuff. I shall mull on this.
Thx again Dan On 3 July 2013 15:10, GESCONSULTOR - Óscar Bou <[email protected]>wrote: > Excuse me for the delay in the response... > > > With respect to Axon, I know you mentioned it before, and I took a look > > through its getting started guide. There's rather a lot of boilerplate > in > > there, which - I've always argued - is liable to slow down / inhibit the > > development of a ubiquitous language. Since you use Axon, you can tell > me > > if I'm mistaken in this or not. > > > > The point with CQRS in general is that the Domain is first modeled through > the Commands (Actions) that need to be available for the user's > perspective, instead of through the Domain Entities "recognized" on the > Domain. > The only "properties" (state) added to those Entities is the ones needed > for executing the commands in the proper context. > > It requires to change one's mind. We've been applying it with a lot of > confidence in some "technical" domains (such as systems monitoring) where > the set of Commands (Actions) required is quite clear and those Domain > Entities are not "so clear" (mainly technical or abstract concepts). Those > Subdomains have strong requirements regarding Command execution scalability > (as those commands can be dispatched by other systems). > > Axon has really, really, really fast implementations for the Command Bus > allowing the processing of thousands of events per second, so it's reallly > good for those purposes. > > As is nearly always told on the CQRS community, on DDD some Subdomains can > be implemented following CQRS, and some others following "traditional > techniques". > > For the "traditional" Subdomains directed towards "Business Users", we > were using a "traditional" Hibernate-based approach (with custom automatic > screen generation from the Domain Entities), and we are moving to Apache > Isis and for now is working really well (still on development). > > > I don't know that the CommandHandler stuff in Axon makes much sense from > an > > Isis standpoint; > > The Command Bus is just used for invoking Actions (i.e., dispatching > Commands) on the context of an Aggregate Root. The steps could be: > 1. "Declare" what you want to do by dispatching a Command (initialized > with the information needed to identify the AggregatedRoot (AR) Entity and > the action's params). > 2. The Command Bus sends to a Command Handler (could be the same AR). > 3. The AR is loaded from the repository by its id. > 4. The AR processes the Command (executes the Action) invoking the proper > method. > 5. The changes made on the state of the AR are saved to the EventSourcing > repository (and all defined Events informing about command execution, AR > state changes, etc. are published). > --- > 6. Other Entities subscribed to those published Events can response by > dispatching other Commands. > > > The fact here is that ALL the API consists only on: > 1. Commands. > 2. Events. > 3. And an interface to work with the Command Bus (dispatching sync or > async Commands) and the Event Bus (subscribing/unsubscribing to Events). > > Sure there are experts here that can give more details or correct me in > any of this points. > > > > On Apache Isis is used the traditional approach to: > 1. Load the Entity from the repository. > 2. Invoke a method on that Entity (or on a Service). > > On those technical domains we have implemented with Axon, we are going to > need an "Apache Isis Service" for dispatching Commands to the Axon Command > Bus, and, probably, another one to handle subscriptions to the Axon's Event > Bus. > > The fact is that we are going to "transform" Service/Entity Actions to the > proper Axon Commands through those Services. > > > If you want, we can provide them to the Apache Isis community. On that > way, Apache Isis will also interact with CQRS Bounded Contexts, being (as > far as I know) the only DDD framework to support it directly. > > > Neither am I certain that an EventStore is the best > > persistence mechanism for the majority of enterprise apps that get built. > > Me neither. > > Think that with an Event Store, you only save the Event notifying a State > change in an AR, not the "full state of the AR". > In order to "reconstruct" the full state of the AR you need to "recreate" > it from the latest snapshot (or the beginning of the times) and apply over > it all changes that were saved after that snapshot. Axon can work with SQL > and noSQL Event Stores, and can automatically create a snapshot each "x" > events published. > > The point here is that you should have Event Handlers subscribed to all > those Events that would "update" the Query database (one or many) that can > have each one, for example, de-normalized structures optimized for > reporting or for OLAP. > > All this makes CQRS a really optimized and scalable system for "executing > commands". And that's its advantage. > > For most enterprise apps is just "too much" engineering (one specialized > database for storing events, one or more specialized databases for querying > - generating the user interfaces, reports, etc.). If they are directed > towards "Business Users" they cannot send Commands at the speeds where CQRS > makes sense. But those "Business Users" have strong query requirements. So > simply the "traditional approach" is just better for me. > > > Hope this helps, > > Oscar > > > > > > > El 30/06/2013, a las 15:55, Dan Haywood <[email protected]> > escribió: > > > Hi Oscar, > > Thanks for these references. > > > > The key thing you said that's helped me was: > > > >> All our Event Handlers are located on previously subscribed Services, so > > they will be in memory. If they need any Domain Object, they query the > > repositories on the event handler. Obviously, multiple Event Handlers can > > be registered for one kind of Event. > > > > That to me seems like the correct solution... don't have individual > > entities subscribing to events, instead have the domain service(s) act as > > the subscriber. As you say, when an event is received, have the service > > identify the impacted domain entities and then call them. That's such an > > obvious approach I feel a bit embarrassed for going down the wrong path. > > > > Anyway... I'm going to refactor the EventBusService and take out that > > subscriber loading/hint stuff. And, then I'll implement a simplified > > version of the @PostsEvent annotation, also without the hint/loading > stuff. > > > > ~~~ > > With respect to Axon, I know you mentioned it before, and I took a look > > through its getting started guide. There's rather a lot of boilerplate > in > > there, which - I've always argued - is liable to slow down / inhibit the > > development of a ubiquitous language. Since you use Axon, you can tell > me > > if I'm mistaken in this or not. > > > > I don't know that the CommandHandler stuff in Axon makes much sense from > an > > Isis standpoint; our RO viewer gives us something like that for free, I > > would've though. Neither am I certain that an EventStore is the best > > persistence mechanism for the majority of enterprise apps that get built. > > But being able to raise events *does* make sense to me as a way of > > decoupling chunks of business logic; hence the EventBusService. > > > > Cheers > > Dan > > > > ~~~~ > > On 30 June 2013 11:38, GESCONSULTOR - Óscar Bou <[email protected] > >wrote: > > > >> Opss!!! Let me resend it. The previous one did not contained the proper > >> links. > >> > >> Just to clarify, when a Command has the @TargetAggregateIdentifier > >> annotation on one of its fields, the Command Bus will previously load > that > >> Entity from the Repository and, after that, will invoke the command on > that > >> Entity. More details on [2]. > >> > >> On the default implementation, the Domain Entity "identifier" field > (which > >> must be compared against the @TargetAggregateIdentifier) is also > annotated > >> with @AggregateIdentifier. The used Repository only needs a "findById" > >> method for locating it and dispatch the command (it's also possible to > >> query specifying also the expected "version", but this is out of scope). > >> > >> > >> Hope this helps again, > >> > >> Oscar > >> > >> > >> > >> ---- > >> > >> Hi, Dan. > >> > >> I'm going to adopt your style of not mixing links with the post. > >> > >> We are currently using as our Domain Event Bus one of the > implementations > >> available on the Axon Framework [1]. > >> > >> As it's a CQRS framework, there is a difference between Command Handlers > >> and Event Handlers. > >> > >> All our Event Handlers are located on previously subscribed Services, so > >> they will be in memory. If they need any Domain Object, they query the > >> repositories on the event handler. Obviously, multiple Event Handlers > can > >> be registered for one kind of Event. > >> > >> For Command Handlers, it's possible to pass a field which contains the > >> Aggregate Identifier (annotated with @TargetAggregateIdentifier), and > also > >> to annotate as a @CommandHandler a constructor method (if the command's > >> responsibility is to create a Domain Entity). Note that only one Command > >> Handler can be registered for each kind of command. If more than one is > >> present, only the last one found will be notified by the Command Bus > about > >> the Command dispatched (detailed info on [2]). > >> > >> There is detailed documentation about Command Handlers [2] (including > the > >> interface for sending Commands) and Event Handlers [3] (they cannot be > >> sent, just received) available. > >> > >> There's a new quickstart tutorial on Axon with a small example based > also > >> on a ToDo domain entity [4]. > >> > >> Hope this helps, > >> > >> Oscar > >> > >> > >> > >> [1] http://www.axonframework.org/ > >> [2] http://www.axonframework.org/docs/2.0/command-handling.html > >> [3] http://www.axonframework.org/docs/2.0/event-processing.html > >> [4] http://www.axonframework.org/axon-2-quickstart-guide/ > >> > >> > >> > >> El 30/06/2013, a las 10:38, Dan Haywood <[email protected]> > >> escribió: > >> > >>> Folks, > >>> Looking for opinions here. > >>> > >>> I recently committed an EventBusService, so that one can > programmatically > >>> post an event via Guava's EventBus: > >>> > >>> public void setStartDate(LocalDate dt) { > >>> this.setStartDate = dt; > >>> eventBusService.post(new StartDateChangedEvent(this, oldValue, > >>> newValue), getFoos(), getBars(), ...) > >>> } > >>> > >>> Guava then invokes any object that has a method annotated @Subscribe > >>> accepting the event type: > >>> > >>> @Subscribe > >>> public void handle(StartDateChangedEvent ev) { ... } > >>> > >>> It's trivially easy for domain objects to register themselves with the > >>> event bus. In their injectXxx method, just do the register: > >>> > >>> public void injectEventBusService(EventBusService ebs) { > >>> this.eventBusService = ebs; > >>> ebs.register(this); > >>> } > >>> > >>> > >>> I'm reasonably happy with all of this, I think, though the downside is > >> that > >>> the publisher needs to ensure that any potential subscribers are > >> in-memory > >>> so that they get notified. This is the purpose of the getFoos() and > >>> getBars() calls passed in to post(...); it's a hint for Isis to bring > the > >>> objects in these collections into memory. > >>> > >>> Also, strictly speaking, the publication of the event should be in > >>> modifyStartDate(...) rather than setStartDate(...) to avoid unintended > >>> side-effects with JDO when it calls setStartDate to recreate or delete > >> the > >>> object. In fact, the JDO objectstore does protect against this, but > it's > >>> all somewhat hacky. > >>> > >>> > >>> ~~~ > >>> > >>> I'm now thinking about introducing an annotation to publish the event, > >> eg: > >>> > >>> @PostsEvent(type=StartDateChangedEvent.class, subscribers={"foos", > >> "bars"}) > >>> public LocalDate getStartDate() { ... } > >>> public void setStartDate(LocalDate dt) { ... } > >>> > >>> > >>> where the "foos" and "bars" are - again - the collections of the > >> publishing > >>> object to be loaded into memory first. > >>> > >>> Or, as a refinement: > >>> > >>> @PostsEvent(type=StartDateChangedEvent.class, > >>> subscribersPolicy=StartDateSubscribersPolicy.class) > >>> public LocalDate getStartDate() { ... } > >>> public void setStartDate(LocalDate dt) { ... } > >>> > >>> would allow a strategy object to be specified to allow arbitrary > >> subscriber > >>> objects to be loaded. > >>> > >>> ~~~ > >>> > >>> I don't think any of this is too difficult to implement, but is it > worth > >>> it? Or is this subscriber loading hint basically telling me that the > >>> approach is fundamentally flawed? > >>> > >>> Opinions welcome > >>> > >>> Thx > >>> Dan > >> > >> > >
