As I briefly touched in my original post, my platform is roughly separated into a front-end webapp and a back-end set of node services. For my original tests, this served its purpose well enough. I was content with keeping a separation of concerns when it comes to processing different types of information, but communication between the various services relied on a common event bus in the front-end. I needed to change that.
My original strategy was based around simply creating a plain Javascript file and running it through node. This was simple, but inefficient. I couldn't take advantage of the neat features offered by babel
and webpack
. I added a new build step to the list, so the build process now includes:
- Main - The main Electron process
- Renderer - The Electron browser process
- Web - Static files
- Backend - The new service module
Instead of creating a Vue component for each service, I now have a single, narrowly-focused component that pipes data to/from the backend. I have removed the service bus completely, and added a test page within my app to monitor data within the service.
I can now refactor my backend to include some neat features from both babel
for ES6 support and webpack
for its additional require
options. It allows me to write a series of interfaces (coming from C++ background) that define common functionality. Currently, I have the following interfaces:
- base-wallet.js
- base-exchange.js
- base-indicator.js
- base-bot-strategy.js
I can utilize ES6's classes to extend these interfaces for each supported module.
- wallets/btc.js
- wallets/doge.js
- exchanges/bittrex.js
- exchanges/gdax.js
Finally, I can load them all in cleanly:
const wallets = {};
const exchanges = {};
const wallet_files = require.context('./wallets', false, /\.js$/)
const exchange_files = require.context('./exchanges', false, /\.js$/)
wallet_files.keys().forEach(key => {
try {
wallets[key.replace(/(\.\/|\.js)/g, '')] = wallet_files(key).default;
} catch(err) {
process.send({message:`unable to add wallet ${key}`});
}
});
exchange_files.keys().forEach(key => {
try {
exchanges[key.replace(/(\.\/|\.js)/g, '')] = exchange_files(key).default;
} catch(err) {
process.send({message:`unable to add exchange ${key}`});
}
});
// Module validation goes here
The biggest benefit here is that I can keep a separation between local and remote backends. My webpack
configuration allows me to build both variations easily, with minor adjustments to account for differences between a local IPC channel and a remote websockets connection.
On IPC
I love how gracefully nodejs handles this. I have attempted to replicate Chromium's IPC setup in past C++ projects, and they became unwieldy and difficult to work with. I would need to hunt down and bring in another library, such as Boost's wonderful IPC library. I would then need to add it to CMake, test the various configurations to ensure it builds properly, and then I can actually use the library. Then I need to deal with creating a shared memory pool, serializing/deserializing objects, and all the associated headaches.
Here, the IPC channel is setup for me the moment I fork the child process. It automatically handles Javascript objects for me
Well written
Very interesting