5

First of all please excuse my english mistakes; it's not my native language. Second, I couldn't find a better title to summarize my inquiry, so let me explain it below:

Let's say we have a software that invoices some products. The invoicing process highly depends on the current legislation. For example, the current law may require that when you create a bill for a client, it is mandatory to have the client's address filled. But the law is expected to change, such that after the 01-01-2018, when invoicing a product you must fill other client's information. This change could be valid until, let's say, 05-05-2018. After this date, the law will change again. However the software must be backward compatible, meaning that after 05-05-2018 you should still be able to create invoices based on the law requirement from 2017, and so on.

Obviously, I cannot add a pile of if-elseif-else statements based on the current date because that won't respect the "Open-Close Principle". What could be a proper design such that the application will be open to be extended, but closed for modifications?

Robert Harvey
  • 200,592

4 Answers4

7

You could use something like the Strategy Pattern for all the cases where behavior may change over time. You would have a component which selects strategies based on the calendar and possibly other factors.

JacquesB
  • 61,955
  • 21
  • 135
  • 189
1

Another option is to use configuration files to drive your reporting. For example, you might have configuration files that specify which fields from your database are printed in which positions on the invoice. The configuration file you choose could be based on the date the invoice is printed, or the date of the sale, or whatever's appropriate.

One simple way to implement this might be to have a specific folder where the configuration files live, and have them be named by the date at which they take effect. (Or maybe the date would be a field inside the file, if that works better.) Then, when creating the invoice, just choose the one for the appropriate date. New laws? Just add more files with the date they take effect.

user1118321
  • 4,981
0

The solution may depend on how much the rules change.

If the different approaches can easily be described by using a few parameters (include_address: true/false, taxRatePercent: BigDecimal, etc.), you could treat them as configuration parameters. In a database (or config file if that offers enough flexibility), store records containing the above mentioned parameters and a start date when a particular configuration becomes effective. Whenever you know that the rules will change, add a record with the new configuration and the appropriate start date (which may be in the future). Finding the configuration effective for current date is quite simple.

Be sure to use a date/time provider interface rather than using the system clock directly since this will allow you to easily unit-test the algorithm for effective configuration selection.

If the rules change in deeper ways, requiring new code for each set of rules, you could refer to the code in your configuration. Handlers for the rules could be registered using some human-readable names and these names could be referred to in your configuration records with start dates. This would be effectively a strategy pattern, but to some extent configurable in property files / database rather than hard-coded.

0

The solution to this conundrum is avoid conflating two independent concepts:

  1. The date of the invoice

  2. The set of rules governing the content

Most of the time the set of rules is determined by the date of the invoice, but not always: your desire to sometimes generate an invoice today using last year's rules. The solution is too make both these ideas explicit in you model somehow. So your function to create an invoice needs both an invoice date and something describing which set of rules to use, which you might also represent as a date. While most of the time the rules date and the invoice date would be the same, you still have the flexibility to have them differ when needed.