Working with events that remove an Aggregate

2,900 views
Skip to first unread message

Marijn Huizendveld

unread,
Aug 28, 2012, 4:12:34 AM8/28/12
to ddd...@googlegroups.com
Hi there,

How do you generally work with commands that ultimately delete an Aggregate? Do you just drop references to it? Do you mark the Events with a DeleteEvent interface so your event store knows not to return the Aggregate?

Cheers,

Marijn

rebo

unread,
Aug 28, 2012, 4:41:27 AM8/28/12
to ddd...@googlegroups.com
I think the solution depends a bit upon the domain you are dealing with, why are AR's deleted?  Can they remain in the store (as persisted events) but never re-used as all references are naturally cleaned up, or will dangling references necessarily remain?

What does it mean to 'delete' an AR, has that aggregate just left a collection, or has it turned into a different type of aggregate?

I Imagine a difficultly will arrise in a say a social networking application, where ARs possibly interface with thousands of other aggregates, and an event store hold pieacemeal information relating to persons.  All of which may need to be deleted due to privacy & data protection regulations.  A challenge indeed!

Marijn Huizendveld

unread,
Aug 28, 2012, 4:51:59 AM8/28/12
to ddd...@googlegroups.com
Thanks for your quick reply :-)

When I say delete I am actually talking about the end of the Aggregates lifetime.
It no longer has any value to the business, we keep the events around for auditing and future insights but we don't want to allow the Aggregates being modified any more.
You might say we don't necessarily want to delete the Aggregate but rather just no longer accept any commands for it.

Yoann R.

unread,
Aug 28, 2012, 4:57:01 AM8/28/12
to ddd...@googlegroups.com
What about a deleted boolean, that you may check everytime you execute some command ?

public void Apply(MyAggregateRootDeleted e)
{
        this._deleted = true;
}

public void DoStuff( string description)
{
    if (!_deleted)
              throw new AggregateRootDeletedException();
    
            var evt = Build.AppaltatoreStuffDone
                          .ForDescription(description);
            RaiseEvent(evt);
}

Deleted might not be the perfect word. It is somehow a bit general. Maybe you can think of something more dedicated to your aggregate root and your domain.

yours,

Yoann

2012/8/28 Marijn Huizendveld <marijn.hu...@gmail.com>

Marijn Huizendveld

unread,
Aug 28, 2012, 5:03:55 AM8/28/12
to ddd...@googlegroups.com
There more I think about it, the more it seems this is something I only want because of some left over CRUD mindset.
The guard clauses are already in place, I just wanted remove them from the AggregateRepository as well.
Not sure why though, cause it doesn't really matter if the AggregateRepository returns an Aggregate that is in read-only/archive mode.

Unless off course, someone here can raise some concerns :-)

@nbarnwell

unread,
Aug 28, 2012, 5:13:14 AM8/28/12
to ddd...@googlegroups.com
If you read Eric Evans' DDD book, you'll find it often talks about "refactoring toward deeper insight". This basically means when you're not sure which way to go when modelling the domain that you should go back and talk to your domain experts. Keep talking as the information soaks in and you'll find yourself picking up on little seemingly throwaway phrases and bits of information here and there that they don't think are particularly special because they're so used to them.

In this case, "delete" might not be a use-case your domain experts need.

Take an example of sales orders (modelled by a SalesOrder AR). If you ask your domain experts "what happens when you delete an order?" they'll likely respond "Oh no - you can't delete orders!" followed by "although you can cancel them or complete them, in which case you can't add any more line items.". In this example "completed" and "cancelled " are the key words, and you'd implement the appropriate invariants in your SalesOrder AR. Of course that implementation may end up as a state machine, but then it's often the case that an AR works like a state machine (i.e. favour a variable "state" rather than a heap of boolean flags).

Remember that DDD is allowing the Domain to Drive your Design. The Domain Experts know it best so they're your best tool. A Domain Model is exactly that - a model of the domain. As such it should reflect behaviours and rules defined by the domain experts and only those behaviours and rules.

HTH

Neil.

Marijn Huizendveld

unread,
Aug 28, 2012, 5:25:30 AM8/28/12
to ddd...@googlegroups.com
On Aug 28, 2012, at 11:13 AM, @nbarnwell wrote:

Take an example of sales orders (modelled by a SalesOrder AR). If you ask your domain experts "what happens when you delete an order?" they'll likely respond "Oh no - you can't delete orders!" followed by "although you can cancel them or complete them, in which case you can't add any more line items.". In this example "completed" and "cancelled " are the key words, and you'd implement the appropriate invariants in your SalesOrder AR. Of course that implementation may end up as a state machine, but then it's often the case that an AR works like a state machine (i.e. favour a variable "state" rather than a heap of boolean flags).

This is already where we are. I'm trying to solve something that is already solved, most likely because I'm still a tech pervert sometimes (although a lot less) :blush:.

@yreynhout

unread,
Aug 28, 2012, 7:02:24 AM8/28/12
to ddd...@googlegroups.com

@nbarnwell

unread,
Aug 28, 2012, 11:48:36 AM8/28/12
to ddd...@googlegroups.com
I hoped someone would turn up Udi's post. I remembered it but not enough to find it. Nice one. :)

Mouhong Lin

unread,
Mar 4, 2014, 9:54:10 AM3/4/14
to ddd...@googlegroups.com
Everytime I google for this subject, I get answers like "turn to ask domain experts". 
But I think there're still times when we need "deletes".
For example, user adds some item by mistake, so he wants to delete it and do it again (it's in the draft status, so we need to at least allow him to delete stuff in the draft status). They just want to do it again from ground up, don't want to modify the existing one (this is true case).

They will complaint if I don't add a "delete" button to that.

Mouhong Lin

unread,
Mar 4, 2014, 9:54:25 AM3/4/14
to ddd...@googlegroups.com
Everytime I google for this subject, I get answers like "turn to ask domain experts". 
But I think there're still times when we need "deletes".
For example, user adds some item by mistake, so he wants to delete it and do it again (it's in the draft status, so we need to at least allow him to delete stuff in the draft status). They just want to do it again from ground up, don't want to modify the existing one (this is true case).

They will complaint if I don't add a "delete" button to that.

On Tuesday, August 28, 2012 11:48:36 PM UTC+8, @neilbarnwell wrote:

Greg Young

unread,
Mar 4, 2014, 10:04:23 AM3/4/14
to ddd...@googlegroups.com
OrderCreated
LineItemAdded
LineItemRemoved
LineItemAdded

I never deleted the first one yet its gone


--
You received this message because you are subscribed to the Google Groups "DDD/CQRS" group.
To unsubscribe from this group and stop receiving emails from it, send an email to dddcqrs+u...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.



--
Le doute n'est pas une condition agréable, mais la certitude est absurde.

Mouhong Lin

unread,
Mar 4, 2014, 10:36:27 AM3/4/14
to ddd...@googlegroups.com

Thanks :) What if I'm not using ES, but use ORM, and I want to notify that a BlogPost is deleted. I can simply delete the blogpost by repository.Delete(post) in the command,  but where is the best place to raise BlogPostDeleted event? Call blogpost.MarkDeleted() which raise BlogPostDeleted before repository.Delete(post)?

You received this message because you are subscribed to a topic in the Google Groups "DDD/CQRS" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/dddcqrs/S1D650oJBDs/unsubscribe.
To unsubscribe from this group and all its topics, send an email to dddcqrs+u...@googlegroups.com.

Greg Young

unread,
Mar 4, 2014, 10:38:40 AM3/4/14
to ddd...@googlegroups.com
Or instead of deleting it set isDeleted=1 and no longer show it (aka soft delete).

But in general ORM + events = a giant pile of #fail

James Nugent

unread,
Mar 4, 2014, 10:39:15 AM3/4/14
to ddd...@googlegroups.com


Thanks :) What if I'm not using ES, but use ORM, and I want to notify that a BlogPost is deleted. I can simply delete the blogpost by repository.Delete(post) in the command,  but where is the best place to raise BlogPostDeleted event? Call blogpost.MarkDeleted() which raise BlogPostDeleted before repository.Delete(post)?

Sounds like you’ll run into model mismatch at some point soon using that kind of system :-)

Mouhong Lin

unread,
Mar 4, 2014, 10:48:29 AM3/4/14
to ddd...@googlegroups.com

Yea, i wanted to move in small steps before cos I don't have ES experience in real projects. So afraid to use it currently. :(

2014-3-4 下午11:39于 "James Nugent" <ja...@geteventstore.com>写道:


Thanks :) What if I'm not using ES, but use ORM, and I want to notify that a BlogPost is deleted. I can simply delete the blogpost by repository.Delete(post) in the command,  but where is the best place to raise BlogPostDeleted event? Call blogpost.MarkDeleted() which raise BlogPostDeleted before repository.Delete(post)?

Sounds like you’ll run into model mismatch at some point soon using that kind of system :-)

Mouhong Lin

unread,
Mar 4, 2014, 10:53:04 AM3/4/14
to ddd...@googlegroups.com

Looks like soft delete is better because I can raise event in aggregate root like others. I do feel the pain of ORM + events. But I don't have real project ES experience, so want to move in small steps so it's easier to control.

Greg Young

unread,
Mar 4, 2014, 10:55:59 AM3/4/14
to ddd...@googlegroups.com
ORM + events is assured for real world pain.

Chris Sampson

unread,
Mar 4, 2014, 11:16:40 AM3/4/14
to ddd...@googlegroups.com

What about document db/no sql + events?

Greg Young

unread,
Mar 4, 2014, 11:17:43 AM3/4/14
to ddd...@googlegroups.com
runs into many of the same problems though not nearly as evil as orm :)

Keith G.

unread,
Mar 4, 2014, 11:19:06 AM3/4/14
to ddd...@googlegroups.com
Greg,
  Could you expand on that statement, that ORM + events = pain? Do you mean ORM objects as event sources leads to problems? The reason I ask is that I've used ORMs in my event handlers to build read models (though not as event sources) and found it to be a pretty successful implementation pattern, as long as one doesn't go down the road of trying to build complex associations as well. (I should note that I work in primarily in Ruby, not C#, and ActiveRecord ends up being a nicer version of a database driver in this case.)

Thanks,
Keith

Chris Sampson

unread,
Mar 4, 2014, 11:21:35 AM3/4/14
to ddd...@googlegroups.com

By problems you mean the same event can be fired twice if trn fails?

Greg Young

unread,
Mar 4, 2014, 11:28:58 AM3/4/14
to ddd...@googlegroups.com
1) how do you raise event and write to document db transactionally?
2) What if docdb and event represent different deltas?

Kijana Woodard

unread,
Mar 4, 2014, 11:33:56 AM3/4/14
to ddd...@googlegroups.com
If one follows those two points to their logical conclusion, "same problems though not nearly as evil as orm" becomes crystal clear.

Luis Cordova

unread,
Mar 4, 2014, 12:51:32 PM3/4/14
to ddd...@googlegroups.com
would be good if you or Kijana do that follow up explicitly to really understand this

Greg Young

unread,
Mar 4, 2014, 12:58:24 PM3/4/14
to ddd...@googlegroups.com
I have about a 40 minute video explaining the problems that come up.

The first is the need for 2pc if I am writing to say a document/database and say a message bus
The second is that I have no way of proving equality between the two deltas I generated and am basically assured to have model divergence between my read/write models. This is a particularly nasty problem especially when you have multiple read models. The problem is that there is no single source of truth.

James Nugent

unread,
Mar 4, 2014, 12:58:53 PM3/4/14
to ddd...@googlegroups.com

> Could you expand on that statement, that ORM + events = pain? Do you mean ORM objects as event sources leads to problems? The reason I ask is that I've used ORMs in my event handlers to build read models (though not as event sources) and found it to be a pretty successful implementation pattern, as long as one doesn't go down the road of trying to build complex associations as well. (I should note that I work in primarily in Ruby, not C#, and ActiveRecord ends up being a nicer version of a database driver in this case.)

That case isn’t the intention of that statement - the problem is if your transactional store is using an ORM and you’re publishing delta events to update read models - you have no guarantee that the delta event you publish and what the ORM did to your database are the same. Using one as a data access layer for read models is fine (though may be overkill!)


James

Kijana Woodard

unread,
Mar 4, 2014, 3:56:03 PM3/4/14
to ddd...@googlegroups.com
Right.

So you can solve the first case by storing the message in the document database. You can solve the second case by keeping all versions of the documents. And then........hey, wait a minute!

Kijana Woodard

unread,
Mar 4, 2014, 3:56:38 PM3/4/14
to ddd...@googlegroups.com
Kind of an ES twist on Greenspun's Tenth Rule.

Maxim Kovtun

unread,
Mar 4, 2014, 4:10:21 PM3/4/14
to ddd...@googlegroups.com

You can do the following for events+ORM:
1. Your repository should have Get and Save only, no add/update/delete
2. Your AR base class should track AR state, such as Created, Loaded&Changed, Deleted
3. AR base class should have protected MarkAsDeleted method, which will set AR state to Deleted
4. Your AR should have Delete method which will fire event + call MarkAsDeleted of base class
5. Your repository in Save method should read the AR's state and execute corresponding insert/update/delete of the ORM.

Mouhong Lin

unread,
Mar 4, 2014, 8:55:24 PM3/4/14
to ddd...@googlegroups.com

Nice idea. +1

Maxim Kovtun

unread,
Mar 5, 2014, 3:58:36 AM3/5/14
to ddd...@googlegroups.com

Also for the consistency of ORM writes and events publishing (aka 2pc), when events are being published from AR, they should be just accumulated in the AR base class, and in the repository, when you have already saved data to DB but not commited transaction yet, you could get pending events from AR and publish them to SB. This is not real 2pc, but might be enought for your needs.

Greg Young

unread,
Mar 5, 2014, 8:45:54 AM3/5/14
to ddd...@googlegroups.com
And if you lose power after publishing but before committing?

Maxim Kovtun

unread,
Mar 5, 2014, 9:27:18 AM3/5/14
to ddd...@googlegroups.com

As i said it is not 2pc :), but as you usually say "what are the chances for the issue to happen, to prevent it now instead of fixing its conciquencies when it happens". I would not use ORM if that was my decision, but the life is tough :).

Nils Kilden-Pedersen

unread,
Mar 5, 2014, 11:19:43 AM3/5/14
to ddd...@googlegroups.com
Be careful though. Some DBs do not enforce constraints until commit, so you might get an optimistic lock failure, which is a much more likely occurrence.
Reply all
Reply to author
Forward
0 new messages