One-to-many relationship between aggregates, how to create/add + interpretation in different contexts

192 views
Skip to first unread message

urbanhusky

unread,
Jun 13, 2016, 8:59:01 AM6/13/16
to DDD/CQRS
Hi,

we're aiming to strangle a large legacy application and utilise DDD/CQRS/ES where applicable because we expect to avoid most of the issues that are associated with the data-centric big ball of mud that is the legacy application.
I'm writing a few prototypes to explore various topics such as CQRS/ES and am aiming to get a better understanding and feeling for applying DDD.

The concrete example I'm currently struggling with can be seen from multiple contexts: master data management and inventory management.
Both share the same concept of warehouses and locations.
  • We have multiple warehouses
  • Each warehouse has multiple locations

I see warehouses and locations as separate aggregates, at least when it comes to inventory management. Locations in the inventory management have invariants regarding as to what can be stored in them.

These are not just events of "placed pallet X in location Y" but we actively can prevent that somebody places a wrong pallet in a wrong location - for example if the location is currently undergoing stocktaking.


A warehouse can also have many locations - especially the larger, automated ones. This would mean that a single aggregate of warehouse + locations would be too big and potentially cause too many concurrency issues.


Currently, I'm trying to event source pretty much everything because I need events for many projections and to communicate across bounded contexts (with proper translation).

I must be able to source the location aggregate from its corresponding events. This is done by implementing a separate stream of events for each aggregate and my projections run off an aggregated stream of all events ($all in Event Store).

This means that the event, which defines the location, must be on the Location aggregate - but I must be able to navigate from the warehouse to its locations, at least on a read model level.


A warehouse might have some invariants regarding the location (there are different warehouse types, which would probably best be modelled through generalisation or similar means). I'm not allowed to define a location for a delivery address of a different customer than the warehouse is associated with (in such warehouses that are associated with a single customer). These are however only really applicable for the master data management (or warehouse management), the inventory management would only use the existing definitions.


My current question is: how can I model the one-to-many relationship between the warehouse and location aggregates?

(oh god I hate the overzealous text editor of google groups. "Hey, let me make this italic, you like italic, don't you? Here, have it italic. Oh you didn't want it italic, I'm sure you just forgot how much you love italic text!")


Since the invariant might be on the warehouse, I need at least a method to define the relationship on the warehouse - which could fail.

var location = /* somehow define the location */
warehouse
.AddLocation(location);

If this succeeds, I would expect to see an Location Added event from/for the warehouse aggregate.

This event however does not define the location or at least in the current implementation of separating events per aggregate it wouldn't.

Therefore I need some way to define the location...


var location = new Location(id, name);
// or
var location = new Location(id, name, warehouseId);

Both of these approaches aren't ideal because they allow to define a location without adding it to a warehouse. I.e. new discovery of an invariant: a location must be part of a warehouse.


var location = warehouse.AddLocation(id, name);


Is this a good idea?

However, now I've manipulated two aggregates in one action and I must know that I did this because I still have to persist and propagate the events that this created.

...with the "events are associated with an aggregate" implementation, this means that I now have to expect a Location Defined event on the location and a Location Added event for the warehouse. The order is also important here.

Technically, this could be solved via a unit of work, but I'm not sure if this is an approach worth pursuing.


What else am I missing here? :)





mynkow

unread,
Jun 14, 2016, 2:08:14 AM6/14/16
to DDD/CQRS
Have you tried to design the location as VO?

urbanhusky

unread,
Jun 14, 2016, 2:16:06 AM6/14/16
to DDD/CQRS
I think that they have to be an aggregate because they must be referenced directly in the process. For example, if a forklift driver puts a pallet into a location, they scan the location barcode and pallet barcode.

urbanhusky

unread,
Jun 14, 2016, 4:05:55 AM6/14/16
to DDD/CQRS
I think my biggest issue here is that I'm trying to interpret and use it in multiple ways, i.e. across multiple bounded contexts.

For the master data management, it would make sense to see the warehouse as aggregate and locations as entities (because a location is identified not just by its values but its identity, i.e. location "A1" might be renamed to "P1" but it is still the same location).

For the inventory/stock side, I need some way to transform the events from the master data management BC so that I get my location and warehouse aggregates in that BC.
...but what would be the best way to do so? Considering I'm using Event Store, it might seem like a good idea to store the translated/transformed events because that allows me to query the exact stream of each aggregate in each BC.
For example I could have streams like masterdata-location-{id} and inventory-location-{id}.
Most projections would have to filter according to BC/tenant. Essentially, I have to put an $all subscription on the border of each BC to handle translations of events from other BC - because Event Store serves as a reliable messaging bus (and we cannot make use of something like NServiceBus or RabbitMQ).

@yreynhout

unread,
Jun 15, 2016, 6:01:17 AM6/15/16
to DDD/CQRS
Reference data does not require duplication inanother context.

urbanhusky

unread,
Jun 15, 2016, 7:59:38 AM6/15/16
to DDD/CQRS
But I might have different representations and views in other contexts. I thought that was one of the main things in DDD, to "duplicate" data because it might also have different meanings?

Kijana Woodard

unread,
Jun 15, 2016, 11:34:41 AM6/15/16
to ddd...@googlegroups.com
Side note.

"...we actively can prevent that somebody places a wrong pallet in a wrong location..."

Be careful with this idea. It seems straight forward, but can lead to bad things (TM). 
The warehouse is the system of record, not the software [HT @gregyoung].

When someone *needs* to put a pallet in a location that's not normally allowed by the rules for "reasons that you can't anticipate now", don't make the user lie about what they are doing. 

Warn and report, don't block.




--
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/d/optout.

urbanhusky

unread,
Jun 15, 2016, 2:23:38 PM6/15/16
to DDD/CQRS
(I might change my mind during this post, so please keep reading)
There are business processes at work here which must prevent users from, for example, loading wrong pallets onto trucks (i.e. wrong item, wrong order, cannot be sold due to QA), that tie into this. There might even be multiple contexts or processes that run inside a warehouse.
It all depends on the context - an automated warehouse for example would, most likely, merely emit events. Anything else that is tied into a process however, must satisfy the invariants.
Pure warehouse movements (moved into location X) would rarely be something that a user couldn't do - however there are some special circumstances where locations are organised in a specific manner. Or you might not want to mix items in a location etc...

I'm pretty sure that even Amazon has rules as to how to store and retrieve items ("that item does not belong to this order, please put it back") and that their barcode/rfid scanners give immediate feedback if the user is doing the wrong or right thing. I would argue that logging an attempt is generally a good idea because it might help find misplaced items (if the user simply disregarded the error and put the item there anyway), but the system has to be protective enough of the business process invariants.

I think that this essentially boils down to:
* In case of a violation/warning, do I assume that the user did not do it anyway
* And would the user confirm not having done it (i.e. "Here, I put it back", "Undo loading that pallet onto the truck")

We have the process as: user scans location, user scans wrong item, scanner communicates error, user tries to do correct thing instead. Or: User scans pallet, user scans loading order, user gets error. User moves pallet to correct ramp and tries again.

However, I think I'm beginning to see what is usually meant when it is said that "warehouse operations are events and always happen". Let me think out loud for a moment:
When a warehouse operation can generate a warning (or rather: error, because it has a higher severity), it should still generate the events that represent the booking. Because the user is currently at that specific location with the specific item.
If he simply leaves it there, it is exactly where the booking list says - even if it really, really should not be there.
If he moves it to somewhere else, it will be booked in the new location... However, this is only possible for concrete units. If I add 50kg of Foo on top of 150kg of Volatile, I can't just remove these 50kg and both the 150kg and 50kg might be contaminated or otherwise mixed. The business process here is then asking for feedback, if that really is the intent or actual state.

So I'm starting to see the reasoning behind this. I'm just put off by the often associated explanation that such things are events only, not commands, because they cannot fail. Also, "reporting" feels a lot like "we will look at it later" - which is why I was so hesitant in the first place. The user must, within a brief time-window, be alerted that the action he just performed is not allowed - this is something that is important and not contradictory to actually making the booking (I explicitly did not say "allow" right now) - with the exception of things that are just not so easily "undone" (not as in "undo" but as in "no longer relevant because we moved it again").

Danil Suits

unread,
Jun 15, 2016, 6:46:22 PM6/15/16
to DDD/CQRS


So I'm starting to see the reasoning behind this. I'm just put off by the often associated explanation that such things are events only, not commands, because they cannot fail.

I can see that.  "Cannot fail" sounds future tense, which isn't quite right.  Such things are events because they have already happened; too bad, so sad -- the only agency your domain model has is deciding if/how to modify its own book of record in response.


The user must, within a brief time-window, be alerted that the action he just performed is not allowed

Maybe "requires remediation" in place of "not allowed"?  The arrow of time is going thataway.


urbanhusky

unread,
Jun 16, 2016, 1:58:22 AM6/16/16
to DDD/CQRS

The user must, within a brief time-window, be alerted that the action he just performed is not allowed

Maybe "requires remediation" in place of "not allowed"?  The arrow of time is going thataway.

Depends on where in the point of the action the command happens. If a user scans the location before placing the item (or dumping a container), we can try to alert in order to prevent something from happening.
 
 
Reply all
Reply to author
Forward
0 new messages