deamon
deamon

Reputation: 92509

Using foreign keys when referencing aggregate roots by id

It is a best-practice to reference another aggregate root by ID and not by reference (see "Implementing Domain Driven Design" page 359 and following).

// direct reference
class AggregateRootA(val b: AggregateRootB)

// reference by ID (preferred)
class AggregateRootA(val b: AggregateRootBId)

class AggregateRootB(val id: AggregateRootBId)
class AggregateRootBId(val id: Long)

The central argument for this kind of decoupling is that each aggregate root should be a transaction boundary.

I wonder whether it is a good or bad idea to use a foreign key relationship in the database when using reference by id in the code. This constraint would enforce consistency at the database level, since there couldn't be a database record of AggregateRootA without the referenced record of AggregateRootB. This is essentially what I want, since the object would be invalid otherwise.

Is there any downside of using a foreign key here apart from slightly more cumbersome testing?

Upvotes: 2

Views: 985

Answers (2)

Eben Roux
Eben Roux

Reputation: 13256

This is too long to add as a comment but I'm just adding to what @VoiceOfUnreason has answered so please do accept that answer and not this :)

Just to be pedantic about it: you are referring to DRI (declarative referential integrity) since even a foreign key relationship could be disabled.

There is nothing inherently wrong with DRI since it has a rather valid purpose. It would, of course, make most sense within the same bounded context as the data would be in the same database. For different Bounded Contexts you could either forego the enforcement of the relationship altogether and work only on the denormalized data or enforce the relationship by storing the value objects in your Bounded Context's data store. Aggregates from a "foreign" Bounded Context would be Value Objects in your Bounded Context. In that case everything that @VoiceOfUnreason mentioned comes into play. In some instances it may make perfect sense to ensure that certain changes to state occur in a reasonable order. For instance, you would probably wish to register a customer before activating the phone line.

For cases where you have less strict rules you could go ahead with a weak relationship and perhaps keep track of your process with a status indicating whether the dependent data is waiting for some other bit to complete which is a typical asynchronous/parallel processing paradigm.

Upvotes: 1

VoiceOfUnreason
VoiceOfUnreason

Reputation: 57279

Is there any downside of using a foreign key here apart from slightly more cumbersome testing?

The constraint introduces some temporal coupling; the database won't allow the write unless the foreign key is actually available, which introduces a "happens-before" relationship between the two.

So if you have a real business concern that the bookkeeping about B has to happen before the bookkeeping linking A to B, then enforcing that constraint at the database is fine.

But that isn't a universal concern, and the foreign key constraint can be painful if the information doesn't always arrive in the natural order.

In short, measure carefully the costs of having the constraint with the benefits of having it.

Upvotes: 5

Related Questions