Phil,
Wow, thanks for that extensive discussion, you've got some nice ideas.
Answers inline.
>> Therefore, we are going to extend our infrastructure to enable it to
>> directly put events into the event store and on the event bus without
>> it going through the usual command/handler/aggregate pipeline.
>
> Why is the request for a document not a command?
Mainly because generating a document is not a domain action, but
rather a transformation of a read model. There is no domain logic at
all in there, no state change; there's just the requirement to track
that the user clicked on the "generate document" button. I'm recording
something that _already happened_.
I could still model it as a command to an "empty" domain, but it just
doesn't match my feeling of what a command is; it is already an event
when it comes to existence.
>> So far so good, now the actual question; since the whole underlying
>> infrastructure (NEventStore, event bus, etc.) assume the notion of an
>> event stream ID/aggregate ID, what stream ID would you assign to the
>> tracking event?
>
> Beware of frameworks imposing non business oriented structure to your
> solution ;)
Yeah. But there is limited time, and in fact the idea that the stream
of tracking events needs some kind of identity is technically okey.
(If it hadn't, that would be more or less identical to my option #1.)
>> 1 - A single stream ID for all such directly fired events without
>> aggregate.
>
> I would recommend against that. To me, an event stream / event source /
> aggregate should be bounded by design. I.e. it principially should not be
> able to grow beyond a certain (possibly unknown) number of events.
> Besides, if you were to use a single ID, that should at least be limited to
> that specific kind of request. But you should be aware that you are
> implicitly creating a "all requests there ever were" aggregate. Does this
> make sense from a business perspective?
From a business perspective, that aggregate doesn't make sense. It
doesn't have a domain counterpart that the user would understand
(other than "tracking records" or "document generation log"). I don't
believe such an aggregate would ever be hydrated. The only reason I'm
storing the events is so that they can be replayed when new read
models are added.
Now, since that is my only reason for storing them rather than just
putting them on the event bus, I should probably view it from that
perspective as well: how would I benefit in replaying from a single
stream ID (#1) vs. a tracking stream ID (#2a) vs. a document request
tracking stream ID (#2b)? There'll probably never be a read model or
business process that depends on _all_ such "global" events; having to
replay them all could however become a performance problem. So no #1.
>> 2 - A dedicated stream ID for all tracking events. Other BCs can use
>> their own stream ID.
>
> 2a: all tracking events: see 1.
> 2b: only request tracking events: see 1.
In the light of what I wrote above, I should probably go for #2b
rather than #2a and #1, since it is the most useful. All read models
generated would usually be interested only in a single category of
events (though there would be multiple event types in that category).
So I'd use the stream ID to categorize events among those lines.
>> 3 - What the heck, just assign it the ID of the corrresponding Person
>> aggregate.
>
> Does the request event modify the bahavior of your Person aggregate? If not,
> it is foreign to it and probably shouldn't go there.
Yep.
>> Are there other good options? What are the pros and cons in your point of
>> view?
>
> 4. You could use a single stream id per request. That would make each
> request its own aggregate. If (and only if) you intend to extend the
> behavior beyond mere registration of the request, this might make some
> sense.
Yes, I could, but I wouldn't really benefit from a request tracking
"item" having its own identity; neither from a business perspective,
nor from a technical one.
> 5. Do you have a requestee modelled? If so, maybe the requests are best
> modelled as part of that?
No, requestees are not modeled. (For now :) )
> 6. Why not use a dedicated tracking store instead of a general event store?
> If your "maybe some business processes" is likely to remain in the maybe
> state for some time, this might well be the easiest option. If you ever need
> to go to 4., you can still generate the events at that time.
Well, that's not much different from #2a, or is it? Since the the
tracking store is more or less a log of request tracking events,
there's not much difference from using a single event stream instead,
I think.
I'd probably go this route if I required specific features from the
tracking store that an event store couldn't give me.
> 7. As an in-between 2b and 4, you could, if you can identify a businesswise
> significant time period, use a single ID for "all requests from (e.g.)
> fiscal year xxx". This is ok technically but still dubious from a business
> perspective.
In fact, this might even be good from a business perspective, since
there might be requirements such as "after three years, the tracking
logs must be purged". I'll need to talk to the domain expert. Thanks
for giving me that idea :)
> 8. As an alternative to 3 (if the correlation between requests and persons
> is the most significant attribute of the request): Use a single ID as a
> stream ID for all requests corresponding to a single person. Do not recycle
> the Person's ID directly, but use a 1-on-1 mapping of Person IDs and Request
> IDs.
I think, from a business perspective, the correlation between requests
and persons is important, but not the most significant attribute. Time
is probably more significant. Which might go more into a direction of
#7.
> Summary: it depends ;) Without further information, I would probably go for
> 6, 5, 8, 7, picking the first that is well applicable, in that order.
Sure, it always does. Thanks for the discussion!
Fabian