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
  • 198,589
  • 55
  • 464
  • 673
  • Possible duplicate of [How does the use of a rules engine impact the design, implementation, and performance of an application?](https://softwareengineering.stackexchange.com/questions/47535/how-does-the-use-of-a-rules-engine-impact-the-design-implementation-and-perfor) – gnat Feb 01 '18 at 12:51
  • @gnat thank you for your reply. However in the post you specified there is about the impact of a rule engine over the performance of the application and from what I understood, the rules can be dynamically added/removed, while my question is about rules that, once added, cannot be changed and the business logic of processing them depends on the current date time. – Teodor Vladutu Feb 01 '18 at 14:14
  • A "pile of if-else" may in fact be a very straightforward and maintainable solution in a narrow context (i.e. in *one or two* places). But if your invoicing differs in other ways, or if the variability permeates the application from end-to-end, you might have to look at some sort of strategy pattern that allows the validation code or the processing implementation to be hived off. – Steve Feb 01 '18 at 14:15
  • @Steve: thank you for your reply. The invoicing system is just an example, but in reality, we have the same business logic spread across some modules, therefore we would have to duplicate the pile of "if-else" statements, which is not a viable solution. – Teodor Vladutu Feb 01 '18 at 14:19
  • @TeoMor *That* is a reasonable reason not to use a "pile of if-elseif-else", not "it won't respect the OCP". – Derek Elkins left SE Feb 01 '18 at 15:05
  • @TeoMor, of course. The next step then is to ask would the "if-else" statements all contain the same logic - because if they do, then you just hive that code block off into its own method which is shared by multiple callers. But if the "if-else" statements would contain different logic at different call sites (but based on the same conditional tests), and that logic is itself reused in multiple places, then that's when you move into the strategy pattern. – Steve Feb 01 '18 at 15:23

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
  • 57,310
  • 21
  • 127
  • 176
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,969
  • 1
  • 17
  • 25
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.

Michał Kosmulski
  • 3,474
  • 19
  • 18
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.