Representing a business' domain in code is a common task for developers everywhere. Oftentimes the same approach is chosen: code everything. But what if there was an alternative method to structure the technical side of things?
Modeling and coding a client’s business ruleset is commonly done by creating domain-classes based on input from a domain expert. For instance, when your client is a car dealer you will code some type of ‘car class’ which holds vehicle data provided to you by the car dealer. This process has the advantage that the business rules of the client are represented in a class hierarchy that is understandable for developers who are familiar with the language that is used.
However, a consequence of this way of domain modeling is that you may end up with a lot of classes that are similar. This makes the configuration difficult to maintain. Additionally, it’s difficult to keep an overview of which class represents what domain subject and identifying existing functionality that can be reused.
Besides coding the complete client-domain, can business rules be represented in an alternative way?
If we were to create everything in code the solution would become enormous in the case of the car dealer. After all, every dealer might want to save different data about the characteristics of their cars. When you code very specific business-related topics directly into your solution, it often means that the code you wrote is only 100% relevant for that specific client’s business. Of course, it could be used for other car dealers as well, but there is a considerable chance that their business rules are slightly different from the ones already coded. This would require additional code to be written that differs per client. This way of working violates the principle of keeping a client-agnostic solution.
One way to deal with this is to create a core that is shared by all clients. This core contains all the client-agnostic functionality and can be used to apply unique business rules for without having to write additional code. Let us take ProductTwo as an example.
ProductTwo is an all-in-one document generator for the financial markets. To learn more about what ProductTwo does, please visit www.ProductTwo.com.*The process of generating complex documents for the financial market by using ProductTwo.*
We currently have a client that sells financial products and uses ProductTwo to generate the term sheets to document transaction details. Those products have certain properties and characteristics that vary from product to product. Regardless of what type is used, the workflow remains the same generic process: data is gathered, validated, and used to generate a Word document explaining the product.
Facilitating this process by using only code would result in a distinct implementation for each product. This means you end up with many (sub)classes, forms, and Word templates. Maintaining that many files will become increasingly laborious after a time. It also makes it difficult for new developers to grasp the scale of the application, understand what products are included, and get an overview of what the solution is capable of.
One way to tackle this problem without coding everything is by distilling the flow that every product follows into a generic process and applying product-specific configuration to feed that process. The configuration in ProductTwo is set up using XML. The nature of XML enables developers to configure hierarchical files that adhere to the rules of an XSD/schema file.
This approach has a couple of advantages:
- Creating a new product can be done by either copying parts of existing products and modifying where needed, or by looking at the XSD file.
- Instead of having to delve through multiple coded (sub)classes to identify what an existing product does we now scan a single XML file.
- All customer-related files can be bundled in a single location.
In Figure 1 (shown below) you’ll find a visual representation of the generic process mentioned above. A product-specific configuration can be inserted into the empty slot to feed the process and modify its behavior.
Figure 1: generic process fed by product-specific configuration.
What kind of data is required to be gathered for this product? This is the first thing defined in the configuration. One product might require a deposit amount, while another product needs a start date. In ProductTwo, the configuration is transformed from an XML-file to a form that can be filled in by a user. Next to a form, another way to gather the required data would be to use an API to eliminate the manual input.
After the user is done filling in the form, the same configuration dictates the validation rules to verify the entered data is in line with the business rules. For example, this could be a check whether the *Start Date* occurs before the *End Date* of the product.
Finally, when all validation checks succeeded, the product’s configuration is used to format and generate the downloadable document (for our first client this is a Word-file).
The logic used in the three steps above does not have any direct relation with any of the configured products. It is flexible and acts solely on the provided configuration file. This allows us to quickly add/modify products without having to worry about the core of the system.
Configuration: the solution to everything?
So, can we conclude that putting business rules in configuration is always the way to go? Certainly not. Configuration has some downsides too:
Creating the core/engine that is completely steerable via configuration takes more time than just implementing the functionality directly. This is a trade-off in time you must consider: do I want (and can I) spend the additional time and resources to make future workflows that are similar easier and faster? When you are not sure whether there will be similar functionality in the future you should tread carefully.
When you are familiar with the self-created configuration format creating new business rules in XML is very easy. However, if you are unfamiliar with the self-created configuration format it can take some time getting used to it. Additionally, a new developer would be required to learn the business rules next to the way they are formulated. When coding your business rules, it only has the overhead of learning the client’s domain (assuming the developer knows how to code...).
There is even an anti-pattern that describes why making everything configurable can be a bad practice called Softcoding. It implies that nothing in your code is hardcoded anymore. Almost everything is configurable which makes it only harder to maintain and understand the solution.
For ProductTwo, the downsides did not outweigh the upsides of having the products configured in XML. It enables us to quickly dish out new products and process incoming feedback.
I hope I managed to give you an insight into another way of representing the business rules of your client. In a follow-up article, I will go into the technical side and explain to you how we structured and iterated on the core that processes the inputted configuration.
Do you have any questions or remarks about this project? If so please contact Vincent Rutten via email@example.com.