Modularising/packaging the portal

Over the last couple of months we've gotten some painful experiences when trying to keep two branches in active development up to date with each other, airbus and master. There's been merges with 700 conflicts. Functionality have been moved manually between the branches and there's functionality on the other end that we would need to do the same with.

That's why we've started a process of splitting up our application into smaller pieces by utilizing composer packages.

Fredrik worked on modular applications in the past and have gifted his valuable experiences in the advantages of making sure our application is modular. The main one's being when we're now onboarding other clients to our application and they all need modifications to the product we have today.

I've been spearheading the work to create modules of our application and I've come a long way already.

The architecture of the modular portal

Development of the portal will be mostly similar to what you do today. There will be a "base" application that offers Laravel to the modules and loads all required modules. This application will take care of "joining" the assets for each individual module via a custom-written laravel-mix-modular package. This ensures that we also get the advantages of code-splitting in our application as our current bundle is a huge bitch.

I've created a custom package for modules osmaviation/module which contains some base functionality for managing modules. In this package there's a ModuleServiceProvider that should be extended by all modules. This supports the convention decided upon for modules, like adding schecules, registering commands, adding listeners and most other things that would normally require you to edit something in the app/ folder.

What I most probably consider the portal today which is the UI into which we log in and most "common" modules will be moved in to the core module. This provides some handy "managers" to register things like menu items, navigator items, data sources and inject HTML-tags into Blade views. The core module also offers the main auth guard used to log in to the portal.

Each of our main products, crewmatch and crewman, will also become dedicated modules instead of being part of the big monolithic application we have today. We are also splitting out jobs, campaigns, lists and widgets to their own modules. This gives us the opportunity to replace the default jobs module with that of an extended one, which is the case for the airbus branch.

The idea is that we do not make modifications to the application (app/) itself to achieve a result.

Of course are still able to do so, in order to extend the application for very specific cases, but the approach would, in general, be modular first.

Each client/variant of the portal would have their own repository with their unique mix of modules.

For example. Airbus has a modified crewmatch variant, so in addition to the crewmatch module we'd need the crewmatch-airbus module for the Airbus-specific modifications. We'd then end up with these dependencies:

{
    "osmaviation/core": "dev-master",
    "osmaviation/crewmatch": "dev-master",
    "osmaviation/crewmatch-airbus": "dev-master"
}

Development

The directory structure would look something like this.

+-- application
|   +-- composer.json
|   +-- webpack.mix.js
+-- packages
|   +-- core
|   +-- crewmatch
|   +-- crewman
|   +-- bulk-action
|   +-- campaigns
|   +-- communication
|   +-- jobs
|   +-- lists
|   +-- notes
|   +-- pay-roll
|   +-- widgeteer

The application would require modules via composer. In development that would mean adding the ../packages path as a repository in the composer.json file. In production this will be solved by a private Satis instance.

To develop for this architecture you'd only have to boot up laravel-mix in the application and that would, with some webpack magic, compile assets from the modules as well.

There would be one dev-version of the portal where you would implement everything rather than switching branches from client to client.

Going forward we should try to encapsulate each module more than what's been done in the past. Currently almost all modules are reliant on each other, which is understandable but unfortunate.

Front-end

Front-end was for me the biggest hurdle for modularising the application. We can gain alot from using Laravel's service container for the back-end code, but for the front-end it was a bit tricky.

What I've come up with is the ability to replace components directly. In the module.json file of each module you'll see a replace section. This should contains an object, where the key is other-module-name@js/someFile.js and the value is the file in the current module to replace the other one with. Everything before the @ in the key would be the name of the other module. This approach gives us flexibility to replace a certain component in a different module in cases where there are modifications.

It is also possible to grab components from other modules quite easily:

import Something from '@other-module/js/something.js'

All front-end paths in the assets start in the resources directory of the module in question.

Current known issues

  • Hot reloading does not work