Skip to content

Design Discussions Modular View Server

chrisjstevo edited this page Oct 9, 2021 · 12 revisions

What do we mean by making the Vuu server modular?

A modular view server would allow us to add tables, rpc services, rest services, custom view port logic and UI components via a simple programmatic or declarative method.

Why is this good?

It means that when a consumer of the view server wants to use it for their project, they have to have little or no knowledge of the view server internals. They can simply reference a binary via a dependency mechanism (such as maven, gradle or node) and use that in their project, with the knowledge that the base server config is functionality is provided out of the box.

But wait, isn't the view server already modular?

Well, yes, on the server side it is already modular. For example a project could reference the binary of Vuu from a maven repository and then add a new module defied in their project as below:

  val config = VuuServerConfig(
    VuuHttp2ServerOptions()
      .withWebRoot("../vuu-ui/dist/app")
      .withSsl("vuu/src/main/resources/certs/cert.pem",
               "vuu/src/main/resources/certs/key.pem")
      .withDirectoryListings(true)
      .withPort(8443),
    VuuWebSocketOptions()
      .withUri("websocket")
      .withWsPort(8090)
  ).withModule(MyCustomModule()) //added my module here...
   .withModule(MetricsModule())
   .withModule(VuiStateModule(store))

This would create all the server side parts required of the view server with the custom MyCustomModule() supplied. (note: we should make the metrics and VuiStateModules added by default.

So what else do we need to do?

Well at the moment there is no way to bind a new UI component that we create to a module, for example if I create a market depth component and want it added to my EquityTradingModule() I can add the market depth table, but there is no easy way to make the framework aware that I have a new UI component and expose it.

To remedy this, one solution would be to declare UI components (or a UI component descriptor perhaps..) in the module alongside the new tables. For example the syntax could be something like:

object SimulationModule extends DefaultModule {

  final val NAME = "SIMUL"

  def apply()(implicit clock: Clock, lifecycle: LifecycleContainer): ViewServerModule = {
    implicit val randomNumbers = new SeededRandomNumbers(clock.now())

    val ordersModel = new ParentChildOrdersModel()

    ModuleFactory.withNamespace(NAME)
      .addTable(
          TableDef(
            name = "instruments",
            keyField = "ric",
            columns = Columns.fromNames("ric".string(), "description".string(), "bbg".string(), "isin".string(), "currency".string(), "exchange".string(), "lotSize".int()),
            VisualLinks(),
            joinFields = "ric"
          ),
          (table, vs) => new SimulatedBigInstrumentsProvider(table),
          (table, provider) => ViewPortDef(
            columns = table.getTableDef.columns,
            service = new InstrumentsService
          ), 
          components = [ 
              "/vui/nodestuff/components/marketdepth.js"
          ]
      )

This approach would tie the component to a table, or table definition, which is likely not desireable.

Another approach would be something like this:

object SimulationModule extends DefaultModule {

  final val NAME = "SIMUL"

  def apply()(implicit clock: Clock, lifecycle: LifecycleContainer): ViewServerModule = {
    implicit val randomNumbers = new SeededRandomNumbers(clock.now())

    val ordersModel = new ParentChildOrdersModel()

    ModuleFactory.withNamespace(NAME)
           .addUiComponent(Name = "MarketDepth.", Source = "/path/to/component")
           //add tables  & joins later..

This has the benefit of the UI components being separately defined outside the scope of a table but within the scope of the module. Which feels more natural if the ui components can reference more than one table at once.

How would the UI know about the components it needs to load in the browser?

There has to be a discovery mechanism similar to tables or columns. One way to do this would be to expose a rest service with the component definitions from the server, something like: https://localhost/api/components, similar to what we do with Vui state? This would return a collection of urls for the components to be downloaded from with some extra meta (todo: ask Steve what meta he'd want...)

{
   components : [
     { name : "MarketDepth",
       code:  "/api/components/<<module>>/marketdepth.js"  
     }
  ]
}

Clone this wiki locally