Changing Perspective: Embracing Temporal Modeling to Capture the Passage of Time
“Time is like a river made up of the events which happen, and a violent stream; for as soon as a thing has been seen, it is carried away, and another comes in its place, and this will be carried away too.” — Marcus Aurelius
Modeling is an important part of software or system development, yet it is not common to see time being used when trying to capture the behavior of a system. When we do, we tend to extract this concept and put it outside the domain in the form of scheduled tasks.
In this article I will advocate for making time the starting point for modeling and propose a departure from the traditional scheduler in favor of a more reactive model.
Traditional Approach
Whenever approaching new development, what is the first — or most common — artifact you build to capture the upcoming work? Chances are it is one of the following:
- Class diagram
- Entity-relationship diagram
- Data model
While there is nothing wrong with any of those artifacts, they all represent or focus on the structure of the things your system will handle over its lifetime. They fail to tell the story of how said things were created and evolved to their current state.
Consider Domain-Driven Design (DDD), where you would be seeking to define the entities, value objects, and aggregates in terms of their behaviors. Even with this approach to software design, temporal modeling proves to be a challenge as these concepts rarely come defined from the domain expert. Instead, they need to be uncovered by iterations of discussions.
Given the aforementioned difficulties, let’s try a different approach, shifting the focus from the structure to what happened over time.
Temporal Modeling
Instead of trying to start from the structure and relationships, temporal modeling tries to discover the intricacies of your domain by exploration. This leads to the structure and relationships, but in a way that is easier to involve others in the discussion.
Let’s look at an example to illustrate one way to apply this modeling. Imagine an e-commerce system where we have a use case that begins when the customer places an order. I will use the event storming notation:
When you look at our example, time was put in the spotlight. We represent how our system evolves as it reacts to facts that have occurred. You focus the discussion on the facts and then evolve to make decisions on how to name the concepts that appeared, and their behaviors.
Now let’s look at when the passing of time itself is an event that you can model.
Passage of Time
In our previous e-commerce system, after analyzing the customer behavior, it has been determined that X% of customers tend to change their minds within 30 minutes of making a purchase. In order to reduce the unnecessary complexity and costs associated with handling those changes after they are processed by the warehouse, a remorse period was introduced. The decision was then captured like this:
Note again that this behavior is represented by a fact: the time has elapsed and the remorse period has expired.
From the implementation point of view, this is normally represented as a scheduled task looking for items that meet the criteria we are looking for:
SELECT orders WHERE creationDate < now — 30min AND status = Approved
This pseudo-SQL can be replaced by whatever method you use in your persistence mechanism. If we have been using events and our application is reactive by nature, why not try to use a similar approach?
Rethinking the Scheduler
The solution using a scheduler — cron for those leveraging a Linux-based system — is the traditional approach. While this option works, it presents some drawbacks:
- May be inefficient. Because it uses a “polling” mechanism, it will likely have to run every minute, running the query even if no order has passed the remorse period.
- Requires you to handle/prevent concurrent executions. If you have a huge number of orders that passed the remorse period, the scheduled task may still be processing the previous batch when the next execution period arrives.
- Weakens the domain knowledge. The remorse period (30 minutes) is an important concept that is nowhere to be found in the domain itself. At worst, it can be scattered between the domain and an external/infrastructure element (the scheduler).
The first two drawbacks are nothing new and arguably have technical solutions, in the form of helper libraries or services, to mitigate their impacts. The last one however transcends the common offerings.
Let’s look at one concept implementation that tries to address all three aspects.
In this proposal, after the order has been approved we will publish an event in the future RemorsePeriodExpired with the expected deadline. After this time has run out, the event will be available for the order system to consume, which will then proceed by internally locking the order to prevent it from future changes.
Testing
A byproduct of modeling time manifests when defining your tests, as they become more explicit and aligned with the language of the use case definition.
Scenario: If remorse period expired, order should be locked
Given OrderApproved
When RemorsePeriodExpired
Then OrderLocked
It is more expressive and easier to write than the alternative which, as we saw, would likely be too close to the infrastructure.
Conclusion
Traditional approaches focused on the structure, even if domain-driven, have limitations and tend to be difficult to engage non-technical stakeholders. On the other hand, other approaches based on storytelling, including the ones based on temporal modeling, encourage the participation of the users by focusing the discussion on the facts that have occurred.
Event storming or story mapping are some of the methods that are recommended and can help to essentially flip things, focusing on the events, timelines, and ultimately leading to the definition of the concepts that will be part of your ubiquitous language.
Leveraging this approach, do not hesitate to capture the passage of time as events if your system has a reactive nature. In business-related processes, where orders, subscriptions or payments are involved, those are more common than you might think.
Ultimately you end up having events, directly or indirectly representing the passage of time, which not only make them first-class concepts, but also use the same message infrastructure your application may already be using.
In a follow-up article, we will present one implementation of the above using AWS Step Function and cloud events. Stay tuned!
Editorial reviews by Catherine Heim & Gregory Belhumeur.
Want to work with us? Click here to see all open positions at SSENSE!