Crypto platform update - Refactoring the backend service

in #crypto7 years ago (edited)

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