Saturday, November 3, 2012

Scala: Enterprise Class Dependency Injection

Scala: Enterprise Class Dependency Injection

I got into a debate some time back about the pros and cons of dependency injection (DI) in a functional programming (FP) language. Tony Morris provided me with a great insight into the views related to the underlying concepts of DI in general as seen from an FP perspective, i.e. they are counter in principle to the overall paradigm. The use of a framework such as Spring is thus inherently counter to the paradigms of an FP . However, I have devised a perfectly viable functional solution to using Spring injection wherein the concepts behind Tony's own monad Reader are employed: Spring beans are injected via a monad that is implemented as a trait, and ensures type safety. The beans themselves are written in Scala, thereby ensuring the FP paradigm is followed to its ultimate conclusion.

Let me back up and explain the need for such a construct. In an enterprise that spans many domains, be it countries, lines of business, or what have you, there are typically solutions which cross-cut them all, yet need tailored via business rules to implement certain domain specifics. An example of this is taxation. Taxes need calculated across every business area within the financials of a global entity, yet they are always domain specific to the region where business occurs. Thus, the concept of "taxes" is common across all domains while requiring regional implementation details for proper calculations. An enterprise class solution should never require multiple deployments to handle this. Furthermore, the separation of Development, QA/Certification, and Production systems means that testing and configuration for said solution will require different data sets and execution environments (albeit the QA/Certification environment should have an exact replica of Production). Enterprise class solutions should have the ability to execute across all domains and execution environments using the same code base. The differentiation between the domains should reside within highly compartmentalized business rules and configurations to allow for execution under any context. This capability allows an entity to deploy the solution globally, yet tailor it to any domain specific requirements. I have found that isolating the domain specific business rules and configurations using an as-needed, run-time selectable methodology is key to implementing such a truly enterprise class solution. This manifests itself under the FP paradigm as a monad (Tony) that lifts in the domain specifics and configurations. However, to keep the code base consistent within all domains, there must be a way to ensure the overall solution resides in a single framework, otherwise multiple copies of it will exist to implement the domain specifics. That is where Spring comes in.

The ability to inject domain specific logic and configurations needed for various execution environments is handled extremely well in the Java world using Spring. In the FP world, using getters and setters is not considered proper, however, as this literally is a side effect being forced upon the code. The answer I devised is to allow Spring to instantiate beans via the application context XML, and then use a monad implemented via a trait to pull them into a class as needed. The profile ability within Spring 3.1.x to select between different sets of the same type of beans makes the selection between execution environments possible without altering any code. Thus, a developer can create multiple tax "engines" implemented as beans and let the domain itself determine which to lift in. Separate profiles for Development, QA/Certification, and Production can be maintained through the implementation of distinct Spring profiles within that same XML. Furthermore, using external property files and a property loading bean allows for the selection of the specific domain. For example:

class Foo extends PropertyLoader with TaxTrait {
  def calculate(income: Double): Double = {
    val domain = properties.stringProperty("domain")
    val taxrate = calculateTaxes(domain)
    //perform some other computations...
    val results = foo(taxrate)

I will not get into the implementation details of the TaxTrait monad at this time which "injects" the business rules bean, I will leave that, and what the Spring context looks like, for my next post as I am out of time currently.

[UPDATE] I have uploaded a complete Maven project showing this functionality in a very generic way (using the tax example from above). You can find it here under the downloads section.

No comments: