Tuesday, June 9, 2015

Pragmatic MDD: Divide and conquer

So last time we found some problems with our code:

- Everything was in one file and it got quite big

- There was no way to customize code generation – e.g. to set the folder where the generated files should be saved

- We didn't have any tests at all (and that is fine for a throw away code, but a must in case we want to maintain it).

Let's see how we can improve the situation step by step.

File is too big? Let's split it.

Before we saw, that our whole process consists of 2 separate steps - model-to-model transformation (m2m) and model-to-text transformation (m2t). So perhaps we should just split these 2 parts into separate files? But why stop there, why not split it into 3 files: One with all the function definitions required for m2m, one with everything for m2t and one that will actually call our m2m and m2t modules, be responsible for the communication with file system and do whatever else is needed. Let's call this third module "runner".

So this is how it will look like. Is not too bad, I think. Such splitting has also one more advantage - testing of the transformations gets incredibly easy. Since all the functions in m2m and m2t modules are side effect free, we can test all of them separately without creating any mocks or writing some complicated setup. Take a look, how the test modules are written.

For the asserts, we are using chai library, as test framework mocha and tests can be executed locally with following command: mocha . --recursive --compilers coffee:coffee-script/register

So far we fixed 2 problems: File sizing, complexity related to it and testing. But what about the customization?

Let's define a separate setup module containing methods, that we will always need, like input folder, where our model is, output folder, where the generated files should be saved and model name (name of the file with the model). Actually, we don't need to define any functions in this module at all - thanks to the dynamic nature of JavaScript we could just add new methods and override old ones on the fly, but still, since we can be quite sure, that these 3 methods will always be needed, let's define them in this module explicitly (even when their bodies will remain empty). Also notice, that we don't define anything regarding namespace here - since namespace will be needed only for such specific transformations as to POJOs, we shouldn't put it in this common setup file.

Now we should add some implementation to empty methods needed for our case. And this is exactly what we do in custom setup file - we are loading our default setup file with empty method bodies, override the old methods and add a new one to provide the namespace for our POJOs.

So far so good.

But how can we access this custom setup file from the runner, where all this information about folders is needed, without referencing it directly?

Via command line.

Take a look at the lines 4 and 5 in the runner. Here first we are loading the default implementation of the setup file with its empty implementations, but then we also load all the modules, that were passed to us via command line.

So if we execute something like coffee runner.coffee ..\..\UseCases\setup.coffee, the application specific setup file will be loaded after the empty setup file, in this way overriding it's old methods and adding new ones. No need for any direct dependencies here.

No comments:

Post a Comment