I've been given the task to design a system that has a product's price have different amounts of different taxes based on different factors.
For example in the US you would get a few - one based on region and another on state and such, while in the EU you get VAT. I'm trying to figure out a non-hardcoded way of different taxes being applied based on different factors for the customer and the product and the location or store.
Does anyone have an idea as to an exiting solution that would easily apply to this problem?
I also would accept general answers on how to learn to solve such design questions in general (like a book that guides you through creating a structured approach)
-
Take a look in the Strategy design pattern, it seems to be a good choice for your problem. If it is not, please provide more details about what you needEmerson Cardoso– Emerson Cardoso10/30/2017 09:50:04Commented Oct 30, 2017 at 9:50
-
Ooh, nice! Yeah, that was kind of what I was looking for, an easy way to add new different kinds of calculating tax based on potentially random factors. I will see how I can adapt it to use this pattern. And I guess I'll have to read up on that Design Patterns book. I may return here to describe my final implementationSpooXter– SpooXter10/30/2017 14:06:07Commented Oct 30, 2017 at 14:06
-
1Don't forget the time aspect, tax rates change over time so the day of sale might affect the calculation too.Lauri Laanti– Lauri Laanti10/31/2017 22:35:01Commented Oct 31, 2017 at 22:35
2 Answers 2
Craig Larman's Applying UML and Patterns tackles the problem with an external tax service (real services do exist if you Google them) and a Gang-Of-Four object Adapter that converts the results into the format expected by the system.
I think Strategy might be OK, but it's ambitious to think that you're going to be able to maintain the code for all the tax algorithms that exist. At every election there's a chance your code will have to change and maybe even have to be validated with local government authorities (?).
As you noted, taxes are complex. You could have multiple lines in a sale (in Quebec there is TVQ and TPS, and at one time you were taxed on the tax!). In some states in the US there is no tax on clothing, or food, etc. To abstract these aspects, Larman's conceptual design used (see Section 26.3) a TaxLineItem
in the domain model. The external service takes care of creating them (the logic is in the external service).
Here's a rough UML diagram of that idea:
For the Adapter, here's how it looks in 26.1:
The use of an adapter:
One solution is to make the various taxes be objects that you add to each product and which are used in calculating its price. For example, you might have an abstract base class or interface that looks like this:
class ITax {
calculateTaxForPrice(Price amount);
};
and then specific subclasses would implement the calculation for different types of taxes, like this:
class VATTax : public ITax {
calculateTaxForPrice(Price amount);
};
class StateSalesTax : public ITax {
calculateTaxForPrice(Price amount);
};
Then your product would have a list of taxes that need to be added to it:
class Product {
public:
addTax(ITax someTax);
Price getPrice();
Price getPriceWithTaxes();
private:
array<ITax> taxes;
};
In getPriceWithTaxes()
you'd take the price and add in the tax for each element of the taxes array.
This scenario lets you exempt specific products from specific taxes, for example. You can have just the ones that each product needs.
-
1What about if I have taxes that are based on both location of the store/customer and the product type?SpooXter– SpooXter10/30/2017 01:15:06Commented Oct 30, 2017 at 1:15
-
That should be fine. The process for adding the taxes will depend on a variety of factors. You'll need to determine for each product, which taxes are appropriate and add them at run time. But it can use the same mechanism as above.user1118321– user111832110/30/2017 02:21:59Commented Oct 30, 2017 at 2:21
-
It may be better to make Product an interface or abstract class, as different product categories will have different tax treatments. The structural challenge, IMO, is to coordinate the different factors that affect the tax: the state, product type, customer type (resale or end-customer), and the time of sale (you may need to backdate a sale and use the tax settings from a different year). The factors combine into a multi-dimensional matrix, but a language's lack of multiple inheritance can complicate the implementation by forcing it down to the 1D nature of single inheritance.user3685427– user368542711/01/2017 14:50:39Commented Nov 1, 2017 at 14:50
Explore related questions
See similar questions with these tags.