The Specification Pattern


Business domains can be very different due to the different natures of each business. Some domains are more static and allow us to easily understand them and produce an efficient core model. However, some business domains are very dynamic, and it is difficult to get the gist and essence of the domain. Some problems require the code to change all the time. The first thing that comes to mind is to externalize business rules either in the form of configuration or in the form of database tables. Externalization to configs and databases works well when the business rules are simple, however, when the business rules are complex, such externalization is more complex and tends to generate more complexity. Nothing is better than code to express complex ideas. Good domain modeling via proper OO design is a very powerful way to map and describe domains.

Design Goals


Let's say we are building a logistics system. We want to transport goods across multiple countries and we need to handle taxes for imports, exports, local tax, state tax, and even country-level tax. Such a problem is very complex. Brazil is probably one of the worst if not the worse country in the sense of taxation. Brazil creates ~35 new tax rules every day in total ~440k tax rules. Brazilians understand chaos and complexity very well, no joke that we have amazing engineers in Brazil. Let's start with an easier part of this problem, let's focus on the sales tax for usa. It will be nice to follow some design goals, also some insights like:

Design Goals

Our solution needs to be simple, we need to end up with something that is easier to maintain. Our overall problem is very complex and change is constant. We might need to deal with tax rules for more than one year, let's say we need to handle re-processing for up to 3-5 years. We can always create a single monolithic class that has millions of IFs, is that what we really want? 

Sure we can push all such complexity to a relational database. Run super complex SQL joins and make the database a huge bottleneck and dont scale at all. Instead, maybe we can model this problem using code. Our code should be self-contained, should be easier to remove classes and add classes, minimizing  SCM conflicts between engineers. 

Enter the Specification Pattern

Domain Driven Design (DDD), is here to rescue you. Using the Specification pattern we can decouple business rules from the code, we can make them dynamic. How can we do it? Simple we will have an interface (Specification) and multiple implementations. We will use a factory to create the right specifications we need. 

Our problem can be modeled in a variety of ways, let's go over some tradeoffs on how we can use the specification pattern properly. My recommendation is that you always run tradeoffs and consider different designs. 

Tradeoffs


Modeling Tradeoffs

We will have taxation tables that will change every year and per state. Considering 2 variables: year and state. It means we have 4 ways to model our solution. The first solution would have all the code in one single class, all years, all states there. We rule out this solution for obvious reasons we want to avoid spaghetti code. 

Our remaining options are we wither break by state, and then have one class by state, in usa it would be 50 states so we would have 50 classes. Inside each class we would have 1 IF per year, consider 3 years, so we would have 3 ifs.

We could break by Year and State. However, it would result in class exposition. Consider 5 years, 50 states and you get 250 classes. Such an approach might not be durable at all because maintenance gets crazy. Such an approach would only make sense if we literally have complex rules per state, let's not talk about Brazil where we have rules per city, per type of industry, and compound taxes. 

The best approach here is to have the model by year. In this case, we would have 3-5 classes only. Every class will have 50 IFs for the states but the main benefit here is that the class is self-contained. Yearly we need to remove one year and add a new one, this is done by deleting one class and adding another one. 

Design

Our logistics system will have a PriceSimulation interface which will be the contract for a PriceSimulation service where we expect to pass a PreOrder and because of some data points, we should give a ballpark estimate on the cost. 

While on the right side of the design diagram we see the PriceSimulationService, the specification pattern is isolated on the left part of the design. We have a PriceSpecification which will provide proper sales taxation given the state. We will have multiple implementations of the PriceSpecification, here we have 2023 and 2022. For convenience, we have a Factory pattern to help us get the right specification for us. The factory pattern is called by the PriceSimulationService. 
Design Diagram

The Specification will look like this:

We need two methods, isSatisfiedBy, tells the caller if the specification is correct for his use case, the method recices the year and the state. Finally, we have the second method, getTax where we will return the proper taxation per state.

You can check out the complete code in my github. My implementation uses HashMaps in order to simplify IF. I do not use Switch states just maps. The DDD specification pattern is very powerful, however, we need to use it with caution, you can introduce complexity by adding abstractions and structures you do not need. So make sure you have a use case where the complexity is truly part of the domain and not introduced by you and your design choices.

Cheers,
Diego Pacheco

 

Popular posts from this blog

C Unit Testing with Check

Having fun with Zig Language

HMAC in Java