Applying Code Generation Approach in Fabrique
Rationale: This paper discusses ideas on applying the code generation approach to help the developer to focus on high-level models rather than on routine implementation tasks. This approach is exemplified by persistence level generation performed by JetBrains Fabrique™. An overall overview of Fabrique is also presented here.
Fabrique – a Code Generator
Briefly, Fabrique is a tool for rapid development of applications with rich client-side web-based interfaces and a database on the backend. Although there are lots of such tools around, all of them represent a big family of "libraries" that provide some additional service but force you to conform to some rules.
For instance, the EJB specification promises to take care of managing persistence for your objects but requires you to follow rules for describing that with code and deployment descriptors. On top of that, different EJB vendors have their own specifics.
The point is that after we choose a platform like EJB, we become dependent on it. That kind of dependency may last for years regardless of whether we are still happy with it. Eventually we may come to regret the decision to go with EJB at all, but after months (or years) of development it's unlikely that we would devote time and resources to switch from EJB to, say, Hibernate. Even if we did, we're still in the same boat. Who says that we will be happier with the new platform, or if we are now, for how long?
However, there is good news. As in our example with persistence, and in many other cases, an actual layer between our code and a target library is pretty formal and can be created automatically. There is no actual need for a human to do this, since following specifications is a routine task. So why not describe what we want with some high-level and platform-neutral language and then let a computer to do the rest? This is how Fabrique is designed.
The main idea of Fabrique is that when the creation of some code becomes a formal routine, it gets generated. The developer only needs to create high-level business models built with a mix of Java and Fabrique- specific languages, and choose which target platform to deploy them to. In this way, Fabrique helps developers focus more on real business solutions and less on technologies that implement them.
The picture below shows this basic principle:
With this approach we are free to decide on an actual platform now or later. Moreover, the right to change our mind is always reserved for us. If we start with EJB and later want to switch Hibernate, we just change the code generator. The application level model does not know anything about the actual platform, so it doesn't require being touched during this process.
Of course, things are not quite that simple. Even with this approach we have a couple of major issues.
First, generated code is likely to be used in manually written code. It's just not possible to remove such dependency since not everything is so formal and routine that it can be generated.
For example, if we have lots of generated EJB classes, we are supposed to use them somehow, right? But what we should depend on from client code? If we depend on generated EJB classes, there's not so much fun with this generative approach. Yes, it's cool that somebody generated those classes for us but the client code is still locked into EJB. So the question is, how can high-level code from an application model use its generated counterparts? If we change the target platform, what happens to our business code that uses some code generated earlier for the previous platform?
The next issue is even tougher: how do we perform refactorings when both generated and hand-written code are affected?
Fortunately, both these issues are solvable. For the solution we have to introduce some more players:
- Generated code should implement some platform-independent interfaces on which hand-written code can safely depend.
- Make refactoring a problem for a refactoring tool rather than the user.