
I wanted to make a simple application of how an event stormed model could be possibly taken into implementation.
I took inspiration from a previous industry I worked in because logistics is really one of those good domains where there is lots of options for interesting modeling.
The scenario I selected is an MVP for calculating shipping costs. It’s going to be an oversimplification just to be able to illustrate two design patterns: the template method and strategy patterns, adding just enough complexity.
Given a package’s weight, the warehouse it comes from and the shipping destination, we want a way to estimate the shipping costs. The company currently ships to Sweden and the UK. We’ll abstract some of the complexity away by having a base cost component which would cover handling and all the costs involved when going from warehouse to destination country, and then the last mile costs which is from the distribution center to the end customer.
Although we have standard rates for our base costs, we have a growing list of partners we work with for our last mile delivery so we want to build a calculator that is able to scale quickly. There’s also been talks that we might be getting a new warehouse in another country so that’s good to have in mind.
After a micro event storming session, it makes sense that we would have a domain service to estimate shipping costs.
I like to start with writing tests because I find it’s easier to figure out the api that will make sense to others, when you’re actually trying to use it.
At this point, I know that the warehouse, the product and destination should play a role and that we will need to be calculating both the base costs and last mile costs. We don’t have to get into specifics to start with since our only focus now should be building up the shipping calculator, so I just create the the input classes and then interfaces for the calculation components.

This test drove the creation of the calculator class:

Branch with full code for reference
Now it gets a little bit interesting. In our model we noted a policy or process exists that determines which strategy applies for a origin/destination country and possibly other future factors.
This was the second test I wrote. I started by creating a Context value object that would hold all the relevant information needed to make decisions further down the process so there will be no more need for fetching data from external dependencies.
I created some initial implementations for both base cost and last mile calculation interfaces. In production, these would be then registered and injected when the Resolver in new’d up.
From the resolver, I expect to be able to call BaseCostCalculatorsFor and LastMileCalculatorsFor and pass in the context I created. I added the assertions one by one while building the implementation that would make it pass.

The resolver’s code is simple, I just had to add a method on the calculator that takes in the context so the calculators themselves can decide whether they apply to a context or not.
I updated the interface for all IBaseCostCalculator to inherit from IContextSpecific which defines the method for AppliesToContext.
For the base costs, they differ by country so the implementation is simple for now:

When the time comes where we have a warehouse in a different country for example, this code would be revisited and enhance to take the origin and destination country into account when calculating base costs.
For last mile calculations, we could have different providers supporting multiple countries. This is a very simplified implementation but we can imagine how variation can be skewed on different factors as well.

Since we have a few possibilities or implementations for our last mile calculations already, we should revisit the implementation of our calculator. For now I’ve taken first, but there’s a lot of opportunity for improvement.
We could get the lowest or highest cost estimates depending on the business, we could take in a selected or preferred provider as apart of the context and have some logic around that selection as well.

In summary, combining the template method with the strategy pattern can enable some really complex implementations of business logic.
Leave a comment