The two given answers (Doc Brown vs Christophe) seem to contradict one another, and I think this has something to do with the ambiguous nature of "can [...]?"
I think Doc Brown and Christophe are both right, but they're answering a subtly different question, and I realize that your question is genuinely ambiguous between the two interpretations. The purpose of my answer is to help disambiguate this.
If you're asking if it's possible to do so in a way that it renders a working application, the answer is yes. Doc Brown is not wrong about what they state in their answer.
If you're asking if this is in line with the spirit/intention of DDD, the answer is no - I agree with Christophe here.
Sharing the same identifier inherently means that you share the same lifecycle, which is the key criterion by which an aggregate root is defined.
Aggregate root is key here. While I thought you had considered this when I first read your question, on re-reading it I realized that you never alluded to the distinction between aggregates and aggregate roots, leading me to wonder if you know the difference between them. It is unclear to me whether you are omitting the "root" verbally but are referring to aggregate roots, or if you're only thinking about aggregates and not aggregate roots.
A very clear indication of this was when you said:
Though it seems it does not make sense to have an unique Id which conceptually be other than the driver's Id.
It sounds to me like you're talking about the aggregate root ID here. Your observation is correct in that sense, because aggregate roots inherently define themselves as being the sensible point of reference for both themselves and any of their (sub)aggregates.
In other words, if Availability
and Location
were an aggregate under the Driver
root aggregate, then you would inherently always fetch an availability (or a location) using the driver ID. The aggregate root acts as the point of reference for all of the data under its root. In DDD, you're generally not allowed to fetch a (sub)aggregate by itself, even if you're only interested in that aggregate. You must fetch the aggregate root, and the specific data you're interested in will be part of the data you receive.
DriverAvailability aggregate, which has a value object containing the status (available/unavailable) and DriverLocation aggregate which has a value object which contains the latitude and the longitude of the driver as well as the timestamp.
Value objects don't need IDs to begin with. That's not to say they can't have any, but it's not inherently required from the domain's perspective. The conclusion here is that you don't inherently need an identifier unless you feel like you need one.
For example, your location value object might be a simple lat/lon pair (two decimal values), and there would likely not be a reason to ever refer to this in any other way than by using the two coordinate values - this is a perfect example of an identifier-less value object.
To Doc Brown's point, if the above paragraph is correct for you, and you're only tracking one location object per driver (or none, when no location has been entered yet), then your persistence layer could indeed be storing the location data in a separate table whose PK happens to be the same as the driver table's PK.
Personally, I'd be inclined to just add these columns to the driver table itself, but there are reasons to pick one over the other and I cannot judge your situation that precisely.
However, somewhat against Doc Brown's point, that has no bearing on the domain and how you design it. From the POV of the domain model, I'd expect a driver to have a Location
property, without there being any particular reference to a second ID value (i.e. not just Driver.Id
but something like Driver.Location.Id
).
Both aggregates seem to change independently, thus it would make sense to be different.
This statement gets very hairy very quickly when considering DDD aggregate roots.
Having a scenario where you only change one subaggregate without changing other subaggregates (or the aggregate root) is not proof that you need a separate aggregate root.
DDD does not prescribe a minimum (or maximum) amount of changed values that can be applied to an aggregate (root) in a single transaction.
With DDD, it's not about discrete updates, it's about lifecycle dependency. The fact that you call it a DriverLocation and DriverAvailability betrays that you consider these value objects as belonging to a driver, hence suggesting that you probably need a Driver
aggregate root with Location
and Availability
subaggregates (or just plain old value objects within Driver
- the distinction between these two options can be arbitrary and subjective).