Applying CQRS

Friday, 22 January 2010 11:21 by ebenroux

Whenever a new technique comes to the fore it takes a while for it to settle in.  Quite often one tries to apply it everywhere and that is when it seems as though it doesn't always fit.  Of course, one should at least *try* to give it a go but there are instances where it may just not be the right tool for the job.

Command / Query Responsibily Segregation is one such an animal.  It may not be applicable in every scenario.

Now don't get me wrong.  CQRS really is the way to go but it works only when there is actually data to query.  Probably seems obvious.  So if the data you require is *not* there and you want to CQRS-it then you first need to create it.  Let me present an example or two.

One may often hear a question along the lines of:

"I need to present the total for the quote / order to the user.  This total will incorporate tax and discounts and other bells and whistles.  It seems as though I need to duplicate my domain behaviour on the front-end.  How can I use CQRS to do this?"

More recently someone asked about repeating calendar events on the DDD group.  Same thing.  The data is not there.  It cannot be queried.  So CQRS does not work when domain interaction is required. 

So how now?  There are two options:

  • Request the domain to calculate the results, persist it, and then query it - CQRS
  • Request the domain to calculate the results, and return it - no CQRS

It really is as simple as that.

So for situations where you do not need, or want, to persist the results get the domain to simply return the transient results and throw them away when you are done. 

Therefore, it is not a case of CQRS Everywhere.

Categories:   CQRS | Database Design
Actions:   E-mail | Permalink | Comments (0) | Comment RSSRSS comment feed

Many-to-Many Aggregate Roots

Wednesday, 9 September 2009 07:53 by ebenroux

There has been some discussion around many-to-many relationships.  Here is what Udi Dahan has to say: DDD & Many to Many Object Relational Mapping

In his example Udi uses a Job and a JobBoard.  As he states the typical modeling for this situation ends up with the Job having a collection of JobBoards and a JobBoard having a collection of Jobs.  This is, as he states, somewhat problematic.  He then reduces the relationship to a JobBoard having a collection of Jobs and a Job having no relation to JobBoards.  This reduction of relationships is more-or-less what Eric Evans also states in his DDD book.

I would like to take another example: Order to Product.  One order can contain many products and a Product can contain many orders.  Yet we don't model it anywhere near an Order having a collection of products or a Product having a collection of orders.

Relationships represented as collections

In this example it may seem obvious since we have all been modeling Order objects for a hundred years.  Everyone should have the idea of an 'extended' association table since we have extra data that needs to be carried in the form of quantity and price.  So we end up with the OrderItem.  Funny that it is not called OrderProduct, yet the OrderItem refers to a product.  That is because there is a natural boundary around an order.  For some reason, these boudaries are not quite that apparent when working with something like a Job and a JobBoard.

Or am I totally off course here?

Temporal Data Coupling

Thursday, 3 September 2009 10:13 by ebenroux

I spent some time this past weekend thinking about how the OO development community has done its utmost to rid itself of high coupling within object models.  It has, for the most part, been rather successful.

So I got to thinking how there appears to be a high level of coupling when it comes to the actual data being stored; so coupling within the database structure.  If one considers that a customer may be invoiced then an Invoice has a direct relationship to a Customer; hence, the customer foreign key resides in the invoice.  This tightly couples the data.  It means that we now absolutely have to have a customer for an invoice.  In a real sense it is probably true.  Although, since we could capture an invoice as a cash sale (for whatever reason) we may not have a real customer.  Be that as it may, we always need to store some form of a customer.

So where is the tight coupling?  I think it turns out to be not so much within the structure but rather it has to do with the temporal nature of the data within the model.  Add to this the fact that we have transactional data (Domain / Command) and 'analytical' data (Reporting / Query) then it becomes quite hectic.

Object State vs. Object Type vs. Object Lifetime

The objects within our broader domain change as time goes by.  Now these changes need to be handled correctly; it may be quite easy to use the state pattern incorrectly.  The archetypal sample of this pattern is the vending machine.  As actions take place within the machine so the state transitions occur.  The vending machine however, stays a vending machine.

Now how about an egg?  Is it a chicken in another state?  Let's say we want to track all our eggs.  Each gets a number.  Then one day egg number 1 'dies'.  Do we care about the egg?  How about the cost of getting the egg to the point it was before it 'died'?  The next day egg number 2 hatches.  Do we care about this egg?  It no longer exists.  We do care about chicken number 1, though.  But after six weeks chicken number one becomes roast chicken number 20.  The chicken is gone.

This is where CQS (Archtecture) comes in.  In our transaction store we could start with the two eggs and track all the required information and reporting data in the reporting store.  Once egg number 1 dies we can remove it from the transaction store.  All the historical data remains in the reporting store.  However, the state of that egg has probably changed to 'dead', though.  The same goes for egg number 2.  It's state changed to 'hatched'; but suddenly we now have a chicken added to our chicken repository.  Once the chicken is roasted we can remove the chicken from our checking repository in the transactional store since all the audit data is still in the query store and the chicken is, well, history.  But now we have an extra item added to the RoastedChickenRepository.

The same idea goes for a Quote leading to an Order leading to an Invoice leading a PaymentReceivedTransaction; each living and dying as it transitions along the path.  So it has a lifetime in our transaction store.  The reporting store, however, never forgets and is the keeper of all things good and bad that happened.

Now the coupling

So let's say we have a 'LastQuote' on our Customer class.  Since our domain model represents our transactional store, *that* reference is only valid while the quote is actually 'alive'.  Once it becomes an Order or the quote is rejected we no longer have a 'LastQuote'.  What do we do?

One option is to simply set LastQuote to null when the quote is deleted; if the last quote actually refers to the quote being deleted.  Now this is where the Don't Delete - Just Don't thinking probably does not apply since deleting from the domain store may just be OK.  Maybe I'm off-track but I'm sure someone will let me know :)

Another option is not to model it like this in the first place but to rather use an application service to return whatever data we need from the last quote, from the query store.  Something like QuoteService.LastQuoteDataForCustomer(123).