DataSet and User Interface
In the last blog post, I discussed how badly for the project is dividing one transaction between multiple events. This scenario is very common in most of the VCL projects and with time it causes huge difficulties with maintaining and upgrading it. This time I'd like to start explaining how to improve this state, and I'm going to build modernization plan.
Remove business logic from GUI
I hope every reader is aware that mixing business logic (data manipulations) with UI actions is a dangerous practice. Then how to extract such business logic or how to remove it from GUI unit (VCL Form)? Maybe you were trying such refactoring, and not achieved satisfactory results. Probably you are expecting that it's a greatly difficult task. If so then I can confirm that it's not very easy, mostly because requires quite deep architectural expertise. But sometimes it's better to learn from your mistakes than still be an unhappy developer, with a nasty VCL project. I will try to guide you through this process.
During project modernization we will have to ship new functionalities that it should be done in an evolutionary way, not braking too much on the occasion. It is tempting to choose the best architectural solution at the beginning, for example, a pure object-oriented solution with great ORM engine, but it's extremely difficult and dangerous road.
We should start with the simplest solution although not very proper one, which will be easy to implement because we are working with a legacy project (written in a classic event-driven way). This road should lead us in the right direction and allow improvements in the future. With time we should discover that continues project refactoring improves its quality, performance and significantly increases developer productivity and satisfaction.
Domain in the TDataModute
As a first step, but not a complete solution, we will move as much domain specific code as it possible into the TDataModule (precisely inside a collection of such modules). Of course, this domain logic will be mixed with data manipulation's code, which is not very proper, although fine. I almost hear the voices of architectural purists: this is a very bad idea! They’re theoretically correct, however, in the world of legacy projects, we need simple solutions, as I said before.
My inspiration to propose this road was the evolution of the project described by Martin Fowler in the "Patterns of Enterprise Application Architecture" book. He defined three patterns: Transcription Script, Table Module, and Domain Model. During refactoring of the Delphi VCL project, we can evolutionary implement those three patterns starting from Transcription Script after that converting parts of datasets using Table Module Pattern (also known as Active Record Pattern) and finished with applying ready to use or implement our own ORM and with help of this, we can implement Domain Model Pattern. Interestingly, in one project these three patterns may coexist.
Implementing Transacion Script Pattern in VCL
The general idea of Transaction Script Pattern is described in the Martin Fowler's blog:
Our first objective is to build some business transactions inside data module object. Quoting Martin Fowler: Transaction Script organizes business logic by procedures where each procedure handles a single request from the presentation.
Why I chose TDataModule as a container for this transaction script methods? We will need a lot of dataset components and it's the most convenient place for them. From the other side, a data module is also an object and a component, thanks for that, we will be able to use inheritance and polymorphism in the future.
Our refactoring plan contains 4 steps which should be iteratively repeated:
- Step 1. Move dataset components from GUI into the data module.
- Step 2. Extract business logic procedures as data module methods.
- Step 3. Decouple the data module – remove UI dependencies.
- Step 4. Clean & refactor code.
I will explain those step in the following blog posts.