I am running into the following design problem, for which I haven't been able to find a satisfactory solution. Any guidance is welcome.
Consider a notion of route for a vehicle, where a route is a sequence of departures or arrivals at known places. For a given vehicle, there cannot be two events at the same time, and a departure must follow an arrival at that same place. One challenge here is the volume of data, as there might in several thousands of events for a given vehicle.
We have two main use cases:
- bulk update, where one replaces a fragment of a route with a new sequence of events
- Analyse the sequence of events (for a given vehicle) to detect various patterns.
What I have tried/thought so far:
- Use a single "route" aggregate for a given vehicle. It is easy, and enables ensuring the consistency of the event sequence, but will not scale, unfortunately.
- Use a "RouteFragment" aggregate that would represent the route of a vehicle between two given timestamps. That partly addresses the scalability issue (hopefully the two timestamps are not too far away), but I am not sure how to ensure consistency across fragments.
- Use a "Stopover" aggregate, which would represent an arrival followed by a departure from the same known place. The problem becomes ensuring that there are no two stopovers that overlap.
- Drop the idea of aggregate, and use some sort of service that would process and clean the sequence of events regularly. The repair logic is not obvious to me either.
Are there patterns or strategies within DDD to manage large volumes of sequential events while maintaining domain invariants? I don't quite see how guidance about aggregates applies (aggregates adhere to transaction boundaries, a transaction should touch only one aggregate, etc.)
How would you have approached that?
It seems there are two things you can apply.
First 'GRASP Controller':
In this case, it may mean that we should handle time-related things outside the domain, at the boundary of the outer layer of an application. It will simplify the task - instead of managing multiple events, you will think about the logic that saves events to DB and get an event or event list from the DB. Also, instead of deduplicating work, you can simply use transactions in DB to avoid duplication.
The next - is "Premature Optimization Is the Root of All Evil."
Memory and CPU time are cheap, and development time is expensive. According to K.Wiegers, increasing performance always decreases modifiability and vice versa. But, if your code is easy to modify, you can always change it for better performance.
So, you can start with the simplest possible thing: a single "route." If you find that its performance is unsuitable for the system, then you can modify it and improve its performance.