Showing posts with label Mendix. Show all posts
Showing posts with label Mendix. Show all posts

Thursday, August 2, 2018

Automating Mendix application deployments with Nix

As explained in a previous blog post, Mendix is a low-code development platform -- the general idea behind low-code application development is that instead of writing (textual) code, you model an application, such as the data structures and the corresponding views. One of the benefits of Mendix is that it makes you more productive as a developer, for certain classes of applications.

Although low-code development is conceptually different from a development perspective compared to more "traditional" development approaches (that require you to write code), there is one particular aspect a Mendix application lifecycle has in common. Eventually, you will have to deploy your app to an environment that makes your application available to end users.

For users of the Mendix cloud portal, deploying an application is quite convenient: with just a few simple mouse clicks your application gets deployed to a test, acceptance or production environment.

However, managing on-premise application deployments or actually managing applications in the cloud is all but a simple job. There all all kinds of challenges you need to cope with, such as:

  • Making sure that all dependencies of an app are present, such as a database for storage.
  • Executing all relevant deployment activities to make an app available for use.
  • Upgrading is risky and difficult -- it may break the application and introduce downtime.

There are a variety of deployment solutions available to manage deployment processes. However, no solution is perfect -- every tool has its strengths and weaknesses and no tool is a perfect fit. As a result, we still have to develop custom solutions that automate missing parts in a deployment process and we have many kinds of additional complexities that we need to cope with.

Recently, I investigated whether it would be possible to deploy Mendix applications, with my favourite class of deployment utilities from the Nix project, and I gave an introduction to the Nix project to the R&D department at Mendix.

Using tools from the Nix project


For readers not familiar with Nix: the tools in the Nix project solve many configuration management problems in their own unique way. The basis of all the tools is the Nix package manager that borrows concepts from purely functional programming languages, such as Haskell.

To summarize Nix in just a few sentences: deploying a package with Nix is the same thing as invoking a pure function that constructs a package from source code and its build-time dependencies (that are provided as function parameters). To accomplish purity, Nix composes so-called "pure build environments", in which various restrictions are imposed on the build script to ensure that the outcome will be (almost) identical if a package is built with the same build inputs.

The purely functional deployment model has all kinds of benefits -- for example, it provides very strong dependency completeness and reproducibility guarantees, and all kinds of optimizations (e.g. a package that has been deployed before does not have to be built again, packages that have no dependency on each other can be built in parallel, builds can be downloaded from a remote location or delegated to another machine).

Another important property that all tools in the Nix project have in common is declarative deployment -- instead of describing the deployment activities that need to be carried out, you describe the structure of your system that want to deploy, e.g. the packages, a system configuration, or a network of machines/services. The deployment tools infer the activities that need to be carried out to get the system deployed.

Automating Mendix application deployments with Nix


As an experiment, I investigated how Mendix application deployments could fit in Nix's vision of declarative deployment -- the objective is to take a Mendix project created by the modeler (essentially the "source code" form of an application), write a declarative deployment specification for it, and use the tools from the Nix project to get a machine running with all required components to make the app run.

To bring a Mendix application in a running state, we require the following ingredients:

  • We must obtain the Mendix runtime that interprets the Mendix models. Packaging the Mendix runtime in Nix is fairly straight forward -- simply unzipping the distribution, and moving the package contents into the Nix store, and adding a wrapper script launches the runtime suffices.
  • We must produce a Mendix Deployment Archive (MDA file) that creates a Zip container with all artifacts required to run a Mendix app by the runtime. An MDA file can be produced from a Mendix project by invoking the MxBuild tool. Since MxBuild is required for this, I had to package it as well. Packaging mxbuild is a bit trickier, as it requires mono and Node.js.

Building an MDA file with Nix


The most interesting part is writing a new function abstraction for building MDA files with Nix -- in a Nix builder environment, (almost) any build tool can be used albeit with restrictions that are imposed on them to make builds more pure.

We can also create a function abstraction that invokes mxbuild in a Nix builder environment:

{stdenv, mxbuild, jdk, nodejs}:
{name, mendixVersion, looseVersionCheck ? false, buildInputs ? [], ...}@args:

let
  mxbuildPkg = mxbuild."${mendixVersion}";
  extraArgs = removeAttrs args [ "buildInputs" ];
in
stdenv.mkDerivation ({
  buildInputs = [ mxbuildPkg nodejs ] ++ buildInputs;
  installPhase = ''
    mkdir -p $out
    mxbuild --target=package \
      --output=$out/${name}.mda \
      --java-home ${jdk} \
      --java-exe-path ${jdk}/bin/java \
      ${stdenv.lib.optionalString looseVersionCheck "--loose-version-check"} \
      "$(echo *.mpr)"
    mkdir -p $out/nix-support
    echo "file binary-dist \"$(echo $out/*.mda)\"" > $out/nix-support/hydra-build-products
  '';
} // extraArgs)

The above expression is a function that composes another function that takes common Mendix parameters -- the application name, the version of MxBuild that we want, and whether we want to use a strict or loose version check (it is possible to compile a project developed for a different version of Mendix, if desired).

In the body, we create an output directory in the Nix store, we invoke mxbuild to compile to MDA app and put it in the Nix store, and we generate a configuration file that makes it possible to expose the MDA file as a build product, when Hydra: the Nix-based continuous integration service is being used.

With the build function shown in the code fragment above, we can write a Nix expression for a Mendix project:

{ pkgs ? import  { inherit system; }                                                                                                                             
, system ? builtins.currentSystem
}:

let
  mendixPkgs = import ./nixpkgs-mendix/top-level/all-packages.nix {
    inherit pkgs system;
  };
in
mendixPkgs.packageMendixApp {
  name = "conferenceschedule";
  src = /home/sander/SharedWindowsFolder/ConferenceSchedule-main;
  mendixVersion = "7.13.1";
}

The above expression (conferenceschedule.nix) can be used to build an MDA file for a project named: conferenceschedule, residing in the /home/sander/SharedWindowsFolder/ConferenceSchedule-main directory using Mendix version 7.13.1.

By running the following command-line instruction, we can use Nix to build our MDA:

$ nix-build conferenceschedule.nix 
/nix/store/nbaa7fnzi0xw9nkf27mixyr9awnbj16i-conferenceschedule
$ ls /nix/store/nbaa7fnzi0xw9nkf27mixyr9awnbj16i-conferenceschedule
conferenceschedule.mda  nix-support

In addition to building an MDA, Nix will also download the dependencies: the Mendix runtime and MxBuild, if they have not been installed yet.

Running a Mendix application


Producing an MDA file is an important ingredient in the deployment lifecycle of a Mendix application, but it is not entirely what we want -- what we really want is a running system. To get a running system, additional steps are required beyond producing an MDA:

  • We must unzip the MDA file into a directory with write permissions.
  • We must create writable state sub directories, e.g. data/tmp, data/files.
  • After starting the runtime, we must configure the admin interface, to send instructions to the runtime to initialize the database and start the app:
    $ export M2EE_ADMIN_PORT=9000
    $ export M2EE_ADMIN_PASS=secret
    
  • Finally, we must communicate over the admin interface to configure, initialize the database and start the app:
    curlCmd="curl -X POST http://localhost:$M2EE_ADMIN_PORT \
    -H 'Content-Type: application/json' \
    -H 'X-M2EE-Authentication: $(echo -n "$M2EE_ADMIN_PASS" | base64)' \
    -H 'Connection: close'"
    $curlCmd -d '{ "action": "update_appcontainer_configuration", "params": { "runtime_port": 8080 } }'
    $curlCmd -d '{ "action": "update_configuration", "params": { "DatabaseType": "HSQLDB", "DatabaseName": "myappdb", "DTAPMode": "D" } }'
    $curlCmd -d '{ "action": "execute_ddl_commands" }'
    $curlCmd -d '{ "action": "start" }'
    

These deployment steps cannot be executed by Nix, because Nix's purpose is to manage packages, but not the state of a running process. To automate these remaining parts, we generate scripts that execute the above listed steps.

NixOS integration


NixOS is a Linux distribution that extends Nix's deployment facilities to complete systems. Aside from using the Nix package manage to deploy all packages including the Linux kernel, NixOS' main objective is to deploy an entire system from a single declarative specification capturing the structure of an entire system.

NixOS uses systemd for managing system services. The systemd configuration files are generated by the Nix package manager. We can integrate our Mendix activation scripts with a generated systemd job to fully automate the deployment of a Mendix application.

{pkgs, ...}:

{
  ...

  systemd.services.mendixappcontainer =
    let
      runScripts = ...
      appContainerConfigJSON = ...
      configJSON = ...
    in {
      enable = true;
      description = "My Mendix App";
      wantedBy = [ "multi-user.target" ];
      environment = {
        M2EE_ADMIN_PASS = "secret";
        M2EE_ADMIN_PORT = "9000";
        MENDIX_STATE_DIR = "/home/mendix";
      };
      serviceConfig = {
        ExecStartPre = "${runScripts}/bin/undeploy-app";
        ExecStart = "${runScripts}/bin/start-appcontainer";
        ExecStartPost = "${runScripts}/bin/configure-appcontainer ${appContainerConfigJSON} ${configJSON}";
      };
    };

The partial NixOS configuration shown above defines a systemd job that runs three scripts (as shown in the last three lines):

  • The undeploy-app script removes all non-state artefacts from the working directory.
  • The start-appcontainer script starts the Mendix runtime.
  • The configure-appcontainer script configures the runtime, such as the embedded Jetty server and the database, and starts the application.

Writing a systemd job (as shown above) is a bit cumbersome. To make it more convenient to use, I captured all Mendix runtime functionality in a NixOS module, with an interface exposing all relevant configuration properties.

By importing the Mendix NixOS module into a NixOS configuration, we can conveniently define a machine configuration that runs our Mendix application:

{pkgs, ...}:

{
  require = [ ../nixpkgs-mendix/nixos/modules/mendixappcontainer.nix ];

  services = {
    openssh.enable = true;

     mendixAppContainer = {
       enable = true;
       adminPassword = "secret";
       databaseType = "HSQLDB";
       databaseName = "myappdb";
       DTAPMode = "D";
       app = import ../../conferenceschedule.nix {
         inherit pkgs;
         inherit (pkgs.stdenv) system;
      };
    };
  };

  networking.firewall.allowedTCPPorts = [ 8080 ];
}

In the above configuration, the mendixAppContainer captures all the properties of the Mendix application that we want to run:

  • The password for communicating over the admin interface.
  • The type of database we want to use (in this particular case an in memory HSQLDB instance) and the name of the database.
  • Whether we want to use the application in development (D), test (T), acceptance (A) or production (P) mode.
  • A reference to the MDA that we want to deploy (deployed by a Nix expression that invokes the Mendix build function abstraction shown earlier).

By writing a NixOS configuration file, storing it in /etc/nixos/configuration.nix and running the following command-line instruction:

$ nixos-rebuild switch

A complete system gets deployed with the Nix package manager that runs our Mendix application.

For production use, HSQLDB and directly exposing the embedded Jetty HTTP server is not recommended -- instead a more sophisticated database, such as PostgreSQL should be used. For serving HTTP requests, it is recommended to use nginx as a reverse proxy and use it to serve static data and provide caching.

It is also possible to extend the above configuration with a PostgreSQL and nginx system service. The NixOS module system can be used to retrieve the properties from the Mendix app container to make the configuration process more convenient.

Conclusion


In this blog post, I have investigated how Mendix applications can be deployed by using tools from the Nix project. This resulted in the following deployment functionality:

  • A Nix function that can be used to compile an MDA file from a Mendix project.
  • Generated scripts that configure and launch the runtime and the application.
  • A NixOS module that can be used to deploy a running Mendix app as part of a NixOS machine configuration.

Future work


Currently, only single machine deployments are possible. It may also be desirable to connect a Mendix application to a database that is stored on a remote machine. Furthermore, we may also want to deploy multiple Mendix applications to multiple machines in a network. With Disnix, it is possible to automate such scenarios.

Availability


The Nix function abstractions and NixOS module can be obtained from the Mendix GitHub page and used under the terms and conditions of the Apache Software License version 2.0.

Acknowledgements


The work described in this blog post is the result of the so-called "crafting days", in which Mendix supports its employees to experiment completely freely two full days a month.

Furthermore, I have given a presentation about the functionality described in this blog post and an introduction to the Nix project:


and I have also written an introduction-oriented article about it on the Mendix blog.

Monday, July 30, 2018

Automating Mendix application deployments with Nix (introduction-oriented blog post)


Mendix is a low-code application development platform. Low-code application development offers all kinds of benefits over traditional development approaches involving code, such as a boost in productivity. For some applications, it is possible to develop up to ten times faster compared to traditional coding approaches and frameworks.

However, despite being different from a development perspective, there is one particular activity that all application development approaches have in common – at some point, you need to deploy your application to an environment (e.g. test, acceptance, or production) to make it available to end users.

For users of the Mendix cloud portal, deployment is automated in a convenient way – with just a few simple mouse clicks, you can make your application available to all potential users in the world.

However, managing on-premise deployments or the cloud infrastructure itself is all but a trivial job – for example, there are many complex dependencies that need to be deployed to run a Mendix application, upgrading may introduce unnecessary downtime and break a system, and the infrastructure needs to be scalable so that it can manage thousands of applications.

Fortunately, there are many automated deployment solutions that come to our aid, such as Kubernetes. Although many of them are useful, none of these solutions are perfect -- they all have their strengths and weaknesses. As a result, there are still complexities we need to solve ourselves and incidents that require fixing.

At Mendix R&D, everybody is encouraged to freely experiment two days a month (the so-called “crafting days”). One of my crafting day projects is to experiment with deployment tools from a different and unorthodox solution spectrum: The Nix project. The goal is to fully automate the deployment of a Mendix application from source – the input is a Mendix project created with the modeler and the end-result is a system running the application.

The Nix Project


The Nix project provides a family of tools that solve configuration management problems in a unique way. Some tools that are part of the Nix project are:

  • The Nix package manager
  • The NixOS Linux distribution
  • NixOps: A NixOS-based cloud deployment tool
  • Hydra: The Nix-based continuous integration service
  • Disnix: A Nix-based service deployment tool

The basis of all tools in the Nix project is the Nix package manager. Nix is quite different from almost any conventional package manager (such as RPM, APT, or Homebrew) because it borrows concepts from purely functional programming languages, such as Haskell.

The Nix Package Manager


The Nix package manager implements a purely functional deployment model. In Nix, deploying a package reliably is the same thing as invoking a pure function, without any side effects. To make this possible, Nix provides a purely functional domain-specific language called the Nix expression language.

{ stdenv, fetchurl, acl }:

stdenv.mkDerivation {
  name = "gnutar-1.30";
  src = fetchurl {
    url = http://ftp.gnu.org/tar/tar-1.30.tar.xz;
    sha256 = "1lyjyk8z8hdddsxw0ikchrsfg3i0x3fsh7l63a8jgaz1n7dr5gzi";
  };
  buildCommand = ''
    tar xfv $src
    cd tar-1.30
    ./configure --prefix=$out --with-acl=${acl}
    make
    make install
  '';
}

The above code fragment is an example of a Nix expression that describes how to build GNU tar from source code and its build-time dependencies:

  • The entire expression is a function definition. The first line corresponds to a function header in which every argument is a build-time dependency:
    • stdenv is an environment providing standard UNIX utilities, such as cat, ls and make.
    • fetchurl is a function that is used to download files from an external location.
    • acl is a library dependency of GNU tar that provides access control list support.
  • In the body of the function, we invoke the mkDerivation {} function that composes so-called “pure build environments” in which arbitrary build commands can be executed.
  • As function arguments to mkDerivation, we specify the name of the package (name), how the source can be obtained (src) and the shell commands (buildCommand) that need to be executed to build the package.

The above expression is a function definition describing how to build something from sources, but the expression does not specify which version or variants of the sources that are supposed to be used. Function definitions alone are not useful. Instead, functions must be invoked with all the required function arguments. In Nix, they need to correspond to the versions or variants of the build-time dependencies that we want to use.

Packages are composed in a second Nix expression that has the following structure:

rec {
  stdenv = import ...
 
  fetchurl = import ...
 
  acl = import ../pkgs/os-specific/linux/acl {
    inherit stdenv fetchurl …;
  };
 
  gnutar = import ../pkgs/tools/archivers/gnutar {
    inherit stdenv fetchurl acl;
  };
 
  ...
}

The above partial Nix expression is an attribute set (a language construct conceptually similar to objects in JSON) in which every key represents a package name and every value refers to a function invocation that builds the package from source code. The GNU tar expression (shown in the previous code fragment) is imported in this expression and invoked with function arguments referring to the keys in the same attribute set, such as stdenv, fetchurl, and acl.

In addition to GNU tar, all build-time dependencies are composed in the same Nix expression. These dependencies are also constructed by following the same convention – invoking a function that builds the package from source code and its build-time dependencies.

In a Nix build environment, you can execute (almost) any build tool. In the GNU tar example, we run a standard GNU Autotools build procedure, but it is also possible to run Apache Ant (for Java software), Python setup tools, Perl’s MakeMaker or CMake and many other tools.

The only catch is that Nix imposes restrictions on what the tools are permitted to do to provide better guarantees that builds are pure, such as:

  • Every package is stored in an isolated directory, not in global directories, such as /lib, /bin or C:\Windows\System32
  • Files are made read-only after build completion
  • Timestamps are reset to 1 second after the epoch
  • Search environment variables are cleared and configured explicitly, e.g. PATH
  • Private temp folders and designated output directories
  • Network access is restricted (except when an output hash is given)
  • Running builds as unprivileged users
  • Chroot environments, namespaces, bind-mounting dependency packages

The most important restriction is the first – in Nix, all packages are stored in a so-called Nix store, in which every package is prefixed by a cryptographic hash code derived from all build inputs, such as: /nix/store/fjh974kzdcab7yp0ibmwwymmgbi6cg59-gnutar-1.30. Because hash prefixes are unique, no package shares the same name and as a result, we can safely store multiple versions and variants of the same package alongside each other in the store.

The result of complementing build tools with these restrictions is that when you build a package with Nix with certain build-time dependencies and you perform the build with the same inputs on another machine, the result will be the exact same (nearly bit-identical) build.

Purity offers many kinds of benefits, such as:

  • Strong dependency completeness guarantees
  • Strong reproducibility guarantees
  • Build only the packages and dependencies that you need
  • Packages that don’t depend on each other can be safely built in parallel
  • Ability to download substitutes from a remote machine (e.g. build server) if the hash prefix is identical
  • Ability to delegate builds to remote machines and be sure that the result is identical if it were built locally

By taking the composition expression (shown earlier) and running nix-build, we can build GNU tar, including all of its build-time dependencies:

$ nix-build all-packages.nix -A gnutar
/nix/store/fjh974kzdcab7yp0ibmwwymmgbi6cg59-gnutar-1.30

The result of the Nix-build instruction is a Nix store path that contains a hash code that has been derived from all build inputs.

Building Mendix Deployment Archives (MDAs) with Nix


As explained earlier, in Nix build environments any kind of build tool can be used albeit with purity restrictions.

For Mendix applications, there is also an important artifact that needs to be produced in the deployment lifecycle – the Mendix Deployment Archive (MDA) that captures all relevant files that an application needs to run in production.

MDA files can be produced by running the MxBuild tool. We can also package MxBuild and the Mendix runtime as Nix packages and write our own Nix function abstraction that builds MDA files from Mendix projects:

{stdenv, mxbuild, jdk, nodejs}:
{name, mendixVersion, looseVersionCheck ? false, ...}@args:
 
let mxbuildPkg = mxbuild."${mendixVersion}";
in
stdenv.mkDerivation ({
  buildInputs = [ mxbuildPkg nodejs ];
  installPhase = ''
    mkdir -p $out
    mxbuild --target=package --output=$out/${name}.mda \
     --java-home ${jdk} --java-exe-path ${jdk}/bin/java \
     ${stdenv.lib.optionalString looseVersionCheck "--loose-version-check"} \
     "$(echo *.mpr)"
     '';
} // args)

The above function returns another function taking Mendix-specific parameters (e.g. the name of the project, Mendix version), invokes MxBuild, and stores the resulting MDA file in the Nix store.

By using the function abstraction and a Mendix project created by the modeler, we can build the Mendix project by writing the following Nix expression:

{packageMendixApp}:
 
packageMendixApp {
  name = "conferenceschedule";
  src = /home/sander/ConferenceSchedule-main;
  mendixVersion = "7.13.1";
}

The above expression specifies that we want to build a project named: conferenceschedule, we want to use the Mendix project that is stored in the directory: /home/sander/ConferenceSchedule-main and we want to use Mendix version 7.13.1.

Using NixOS: A Nix-Based Linux Distribution


One of the common objectives that all tools in the Nix project have in common is declarative deployment, meaning that you can express the structure of your system, and the tools infer all the activities that need to be carried out to deploy it.

As a Mendix developer, generating an MDA archive is not entirely what we want – what we really want is a system running a Mendix application. To accomplish this, additional deployment activities need to be carried out beyond producing an MDA file.

NixOS is a Linux distribution that extends Nix’s deployment features to complete systems. In addition to the fact that the Nix package manager is being used to deploy all packages (including the Linux kernel) and configuration files, it also deploys entire machine configurations from a single declarative specification:

{pkgs, ...}:
 
{
  boot.loader.grub.device = "/dev/sda";
  fileSystems."/".device = "/dev/sda1";
 
  services = {
    openssh.enable = true;
 
    xserver = {
      enable = true;
      displayManager.sddm.enable = true;
      desktopManager.plasma5.enable = true;
    };
  };
 
  environment.systemPackages = [
    pkgs.firefox
  ];
}

The above code fragment is an example of a NixOS configuration file that captures the following properties:

  • The GRUB bootloader should be installed on the Master Boot Record of the first harddrive (/dev/sda)
  • The first partition of the first harddrive (/dev/sda1) should be mounted as a root partition
  • We want to run OpenSSH and the X Window System as system services
  • We configure the X Window Server to use SDDM as a login manager and the KDE Plasma Desktop as desktop manager.
  • We want to install Mozilla Firefox as an end-user package.

By running a single command-line instruction, we can deploy an entire system configuration with the Nix package manager:

$ nixos-rebuild switch

The result is a running system implementing the configuration described above.

Creating a NixOS Module for Mendix App Containers


To automate the remaining Mendix deployment activities (that need to be carried out after composing an MDA file), we can create a systemd job (the service manager that NixOS uses) that unpacks the MDA file into a writable directory, creates additional state directories for storing temp files, and configure the runtime by communicating over the admin interface to start the embedded Jetty HTTP service, configure the database and start the app.

Composing a systemd job can be done by adding a systemd configuration setting to a NixOS configuration. The following partial Nix expression shows the overall structure of a systemd job for a Mendix app container:

{pkgs, ...}:
 
{
  systemd.services.mendixappcontainer =
   let 
     mendixPkgs = import ../nixpkgs-mendix/top-level/all-packages.nix { inherit pkgs; };
     appContainerConfigJSON = pkgs.writeTextFile { ... };
     configJSON = pkgs.writeTextFile {
       name = "config.json";
       text = builtins.toJSON {
         DatabaseType = "HSQLDB";
         DatabaseName = "myappdb";
         DTAPMode = "D";
      };
    };
    runScripts = mendixPkgs.runMendixApp {
      app = import ../conferenceschedule.nix { inherit (mendixPkgs) packageMendixApp; };
    };
  in {
    enable = true;
    description = "My Mendix App";
    wantedBy = [ "multi-user.target" ];
    environment = {
      M2EE_ADMIN_PASS = "secret";
      M2EE_ADMIN_PORT = "9000";
      MENDIX_STATE_DIR = "/home/mendix";
    };
    serviceConfig = {
      ExecStartPre = "${runScripts}/bin/undeploy-app";
      ExecStart = "${runScripts}/bin/start-appcontainer";
      ExecStartPost = "${runScripts}/bin/configure-appcontainer ${appContainerConfigJSON} ${configJSON}";
    };
  };
}

The above systemd job declaration does the following:

  • It generates JSON configuration files with app container and database settings
  • It composes an environment with environment variables configuring the admin interface
  • It launches scripts: one script before startup that cleans the old state, a start script that starts the app container and a script that runs after startup that configures the app container settings, such as the database

Writing a systemd job as a Nix expression is quite cumbersome and a bit impractical when it is desired to compose NixOS configurations that should run Mendix applications. Fortunately, we can hide all these implementation details behind a more convenient interface by wrapping all Mendix app container properties in a NixOS module.

By importing this NixOS module in a NixOS configuration, we can more concisely express the properties of a system running a Mendix app container:

{pkgs, ...}:
 
{
  require = [ ../nixpkgs-mendix/nixos/modules/mendixappcontainer.nix ];
 
  services = {
    openssh.enable = true;
 
    mendixAppContainer = {
      enable = true;
      adminPassword = "secret";
      databaseType = "HSQLDB";
      databaseName = "myappdb";
      DTAPMode = "D";
      app = import ../../conferenceschedule.nix {
        inherit pkgs;
        inherit (pkgs.stdenv) system;
      };
    };
  };
 
  networking.firewall.allowedTCPPorts = [ 8080 ];
}

The above code fragment is another NixOS configuration that imports the Mendix app container NixOS module. It defines a Mendix app container system service that connects to an in-memory HSQL database, runs the app in development mode, and deploys the MDA file that is the result of building one of our test projects, by invoking the Nix build abstraction function that builds MDAs.

By running a single command-line instruction, we can deploy a machine configuration running our Mendix application:

$ nixos-rebuild switch

After the deployment has succeeded, we should able to open a web browser and test our app.

In production scenarios, only deploying an app container is not enough to make an application reliably available to end users. We must also deploy a more robust database service, such as PostgreSQL, and use a reverse proxy, such as nginx, to more efficiently serve static files and cache common requests to improve the performance of the application.

It is also possible to extend the NixOS configuration with a PostgreSQL and nginx system service and use the NixOS module system to refer to the relevant properties of a Mendix app container.

Conclusion


This blog post covers tools from the Nix project implementing deployment concepts inspired by purely functional programming languages and declarative programming. These tools offer a number of unique advantages over more traditional deployment tools. Furthermore, we have demonstrated that Mendix application deployments could fit into such a deployment model.

Availability


The Nix build abstraction function for Mendix projects and the NixOS module for running app containers can be obtained from the nixpkgs-mendix repository on GitHub. The functionality should be considered experimental – it is not yet recommended for production usage.

The Nix package manager and NixOS Linux distribution can be obtained from the NixOS website.

This blog post originally appeared on: https://www.mendix.com/blog/automating-mendix-application-deployments-with-nix/)

Tuesday, June 19, 2018

My introduction to Mendix and low-code application development

As explained in my previous blog post, I started a new challenge two months ago and joined Mendix, a company that develops a low-code application development platform. Because I am receiving many questions about the platform and low-code application development in general, I have decided to write a blog post about it.

Low-code application development


With Mendix, the idea is that instead of writing (textual) code, applications are developed by modeling (and configuring) various kinds of application aspects, such as the data model and graphical user interfaces.

Why would that be useful you might ask? It makes you a developer more productive.

As many seasoned software developers may probably already know, developing applications is typically a costly investment and time consuming process. Aside from implementing all desired functionality and making it work correctly, a developer typically also needs to solve many kinds of secondary issues, such as:

  • Memory management.
  • Interacting with a database for storage.
  • Pagination: Displaying a fixed amount of rows on a page for a collection of data
  • Making screens work on various kinds of displays, e.g. monitors, tablets, phones.
  • Deploying the application to a production environment so that it can be used by (possibly) a large number of end users.

For certain classes of systems, such as information systems, these secondary issues are frequently implemented over and over again, wasting precious development time and increasing the likelihood on errors.

With Mendix, most of these secondary concerns have been addressed in a reasonable manner, and primary functionality can be implemented by modeling and writing none to just a tiny amount of code, providing an incredible boost in developer productivity.

In addition to productivity, another objective of the Mendix platform is to reach a broader audience than just developers, such as business consultants.

A simple development scenario


What does the development process of a very tiny Mendix application look like? Typically, an application development process starts by creating a data model that specifies what kind of data needs to be stored (either in a database or in memory).


As shown in the picture above, in Mendix, a data model is created by drawing an ER (Entity-Relationship) diagram. For example, the example above defines a 'Contact' entity representing a contact person (with properties: first name and last name) and a 'Message' entity representing chat messages. A contact can send zero or more messages, as denoted by the 'Message_Contact' relationship.

After defining a data model, you may want to develop screens containing user interface components allowing users to inspect and change the data. In Mendix, these screens can be generated with just a few simple mouse clicks: creating a blank page, dragging and dropping a data grid to the page, and dragging and dropping the 'Contact' entity to the data grid:


In addition to an overview page displaying a collection of data items, we also want a page allowing us to change a record or to create a new record. This screen can be created by right clicking on the 'New' button and picking the 'Generate page...' option. The result is a page allowing us to edit an individual contact record:


Finally, to make the screen available to end users, we must add it to the navigation layout by creating a button that redirects the user to the overview page.

The result


With just a few simple clicks, we have already constructed a very small, but working application. For example, when clicking on the 'Run locally...' button in the IDE, we can start an instance of our test app and run it in a web browser.

The overview page looks as follows:


As may be observed by looking at the image above, the screen offers all functionality needed to work with a collection of records: navigation buttons, a search function, pagination, and edit functionality.

By clicking on an item in the list or clicking on the 'Edit' button, we can navigate to a page allowing us to edit the properties of a record:


After changing any of the attributes and clicking on the 'Save' button, the changes will be committed to the database.

Implementing all this functionality did not require me to write a single line of code.

Programming additional functionality


In addition to defining the domain model and generating views for all data elements, we may also want to program additional functionality, for example, a method that counts the amount of contacts having a first name that starts with an 'S'.

In Mendix, server-side functionality can be programmed by constructing microflows. Microflows use a graphical notation based on the Business Process Model and Notation (BPMN), a standardized graphical notation.


The above microflow retrieves all contacts from the database, sets an initial counter variable to 0 and iterates over all contacts. When a first name that starts with an 'S' has been encountered, the counter will be increased. Finally, it will return the counter value to the caller.

Microflows can be used to program many kinds of aspects in an application -- for example, they can be launched on startup, attached to buttons as event handlers, or attached as pre- or post commit hooks to data entities.

Other features


Besides the features described in this blog post, the Mendix platform has many additional features to offer, such as access control, internationalization, custom page layouts and collaboration tools (with a Subversion-based team server, and sprintr application managing the development workflow).

Furthermore, the deployment process of a Mendix application is completely automated in the Mendix cloud -- with just a few simple mouse clicks, your application becomes publicly available, without having to worry about managing a database or application server.

Finally, the platform is extensible -- you can implement custom actions in Java (server-side) and your own widgets in JavaScript (client-side) and there is an app store allowing you to download all kinds of pre-built third-party extensions.

Availability


You can sign up for a free Mendix account to start experimenting.