Hi Sebastian!
Great question! Since I myself considered doing a similar pet project
for ED, I've had similar thoughts.
Since we are in the context of CQRS, and not just DDD, on this list,
that needs to be accounted for. I.e. how do you design the write
model, and how do you design the read model. And what is an aggregate
anyway?
An aggregate is a function from command to a sequence of events, and
acts as the transactional boundary. That's on the write side. On the
read side, you would typically get the sequence of events and apply
them to a database. How you decide to design that database is entirely
up to you. If you use a relational database, then using foreign keys
directly to entities within aggregates is totally fine. Or if you're
like me, using a graph database (Neo4j), then entities have direct
relationships between them, so I can easily do graph queries to find
what I need.
The rule to not have direct references is, to me, only for the write
model. Simply because the way we apply commands is to load the
aggregate for the command, apply the command, and store the events. If
that aggregate load is done through event sourcing, then those events
would (by nature) only contain identifiers, so there are no hard
references, to anything (including entities). If the load is done by a
database call, then you wouldn't want it to load anything outside of
the aggregate, or it would just be too costly. Also note that if you
do a database load for the aggregate, you only need to load the
snapshot state needed to make decisions on how to translate commands
into events. And that might be a small fraction of the total state, it
certainly is for me in pretty much all cases.
So that's the basics. But in your case, and I'm familiar with the
domain as mentioned above, the main input is going to be data from
another system, let's call them events, that you want to ingest.
Events from another system ("this has happened") are turned into
commands in your system ("I want this to happen") that you can then
turn into internal events ("this has happened, as far as this system
is concerned"). You may want to do validation of these external events
(is the field format ok, are they references valid, etc.), but I would
do that in the step that converts external events into internal
commands. So when the internal commands are sent to the aggregate it
can assume they are valid, and the main concern becomes translating
into internal events, zero or more. You would store these events in an
event store, have a read model subscriber read them and apply to your
read database for queries, and that's pretty much it.
From this point of view, you are probably not using the fact that an
aggregate is a transactional boundary all that much, as there are very
few constraints you need to enforce (if you trust the source of the
events to be correct, and validation has fixed the rest). You might as
well make everything aggregates. Or not. Doesn't really matter, right?
You could go the other extreme and make the entire galaxy an
aggregate, and again, wouldn't make much difference.
Unless, of course, you want to apply a bunch of rules, which requires
a lot of state, in which case your aggregate boundary will determine
how much your aggregate has access to in order to make the decisions.
That can be worked around though, by having the event->command
translator also provide any state needed from the read database in
order to make those decisions.
So, I think your question is ultimately less related to DDD/CQRS and
more towards event sourcing, because of the (presumably) lack of
domain rules and constraints. That's going to be a more practical way
to look at it.
HTH and YMMV,
Rickard
> --
> 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.
> Visit this group at
https://groups.google.com/group/dddcqrs.
> For more options, visit
https://groups.google.com/d/optout.