Modular programming in Mendix
What and why
In Mendix-projects, or any software project for that matter, with each addition to an app, the code grows, gets more complex, techdebt increases and effort needs to be invested to keep the app alive. And since building long-lasting applications is both more valuable and satisfying compared to getting your work replaced every few years, this can help to improve the software’s longevity: Modular programming.
One of the perks of Mendix is the ability to separate functionality into modules. This allows us programmers to increase the software’s score on the quality characteristics ‘maintainability’ and ‘portability’. See iso 25000i and iso 25010.
For example when working on a story "As a User at the end of process xyz I want to get informed via email and/or SMS", you dive in, create two microflows 'Emailmessages' and 'SMSmessages' having some parameters and doing the work and you trigger them where needed. A short time later you have it up-and-running and the customer is happy. All is well, but then the problems start. Your customer wants to include other message-services as well. Also some errors have occurred and they want quality assurance and error handling. Also they want to switch from email-provider. During the refinement-session your team decides to make a module that handles outgoing communication. It has a function that only takes a message, a receiver and a message type and takes care of the rest, including errorhandling when the message does not reach the receiver.
This setup has these advantages:
- taking care of outgoing communication is no longer a concern for your project, but only for the module;
- adapting to new ways of communication only requires adding those new communication techniques to the module, not your project;
- your project stays clear of the complexity of calling the outgoing communication;
- further development of the outgoing communication, like retrying failed emails or sending SMS in cheaper bundles, can take place without your project getting touched. Your project's functionality expands as the module does;
- other projects can use the standalone communication module as well and even improve it and/or extend it with extra features.
- testing of sending messages of any type is limited to the module
- errors and logmessages will be more specific
So now you need to create a module. Here you go:
- Create a module in your project, simply right-clicking the top folder called project, automatically it will have it’s own domain
- Give it this structure:
- In _UseMe create:
- Snippets that are meant to incorporate on some project page outside the module
- A snippet that allows the administrator to change the constants. Make this snippet have descriptions, default values and optional values so it will be self-explanatory, unambiguous and clear.
- A ReadMe-file containing information on how to use the module. Refer to the configuration snippet for configuration-constants and their possible values. If you have your module stored in the (company-)AppStore, then it is better to have the readme only contain a reference to the AppStore module's documentation and maintain your information there.
- In Private:
- Create a folder 'Configuration' for containing all constants in this module that will contain the default values of the module's configurable parameters. N.B. in Mendix the value of a constant varies per environment. They are actually used as environment-parameters. Might be useful to include a microflow 'Configuration_NewOrOpen', that will retrieve the configuration-settings.
- Create one lognode-constant if your module has limited functionality. If you require more log nodes, than create an enumeration Enum_Lognodes for the Lognode-texts that you can use in your AppStore module. Use this lognode in the logmessages in your module.
- Let the pages of your module refer to the generic company-layouts, likely the Atlas-ui or a derivative thereof. If none is present, use a module-specific layout. Do not use a layout of any other module because that will create an unwanted dependency.
- Keep the module small. Only implement the module's own functionality, nothing else. Single responsibility as in the SOLID-principleii.
- Module-specific resource-items: Put resource items in folder /resources/<yourApp_inlowercase>
- Use as little cross-module references as necessary, preferably none. Instead have the project communicate with the module by calling the module’s microflow and return values.
- Add a test-folder containing the all the tests that your module has to comply to. Test-microflows, SOAP-UI project or Postman-project or any other test you can think of.
It can be useful to upload your module to the appstore, either public in Mendix’s or private in your company’s appstore. This will allow you to use it in other projects without modification. If you intent to do so, also add a version number mentioning both the mendix-version and it’s own, for instance by adding a folder named “Mx7_18_Version_1.0.0”. If your module requires any jar, then in the /userlib add an empty file <jarfilename>.jar.<yourApp_inlowercase>.RequiredLib, just to let this requirement be known. Before releasing you, of course, check your module on errors and warnings and resolve all of them. You might even use an external software-quality-tool like OmNext and don’t forget the text-element translations, if your module serves more than one language.
You will also need to choose a version for your module:
As low as it's functionality allows you, that will make the module available to lower-releases-Apps.
As high as possible will allow using the new functionality and profiting from bug-fixes.
As general rule of thumb you should take the version of the lowest-release-App that you aim to write the module for.
Modifying your module in another, lower, Mendix version has the disadvantage of having two modelers open at times, one for your module, one for your project. Whenever you have made some changes to the module, you transfer it to your project via export and import, thereby overwriting the module in your project, time-and-time-again, which is tedious and time-consuming. However, mostly you will be working on either one of the two, but not both, since they are functionally segregated. That will keep the number of export-import repetitions acceptable.
Upon completion of any change you can decide whether or not to update the module in the AppStore.
Modular programming rocks
In short, it will increase the quality of your work, reduce number of errors and increase the lifetime of your product. Happy programming everyone.
ii Robert C. Martin describes the SOLID ‘Single responsibility principle’ in 2014 on https://blog.cleancoder.com/uncle-bob/2014/05/08/SingleReponsibilityPrinciple.html