Architecture - AutolabJS/autolabcli GitHub Wiki

Current Module Structure

module-structure

Direction of Dependency

Robert C Martin talks about three concentric layers in software: domain model, use cases and user interface. The innermost layer is domain model. The middle layer is use cases; use cases depend on domain model and domain model does not know anything about use cases. Similarly, user interface is the outer most layer. Use interface knows about uses cases and not the other way round. So, when we port software across different views, we only need to change the user interface.

layer sketch

Above diagram illustrates the use case layer as a service layer. Both are synonyms.

To map these concepts to Autolabcli, we have the following.

  • data source layer ---> missing
  • model ---> missing
  • service / use cases ---> lib/model
  • user interface ---> lib/controller + lib/view

We have written use cases without having a coherent domain model. So if we look closely, we will find repetitive functionality among different use cases. So creating a coherent domain model will remove repetitive code from lib/model.

We can apply the following refactoring.

  • data source layer ---> lib/data (connection management to AutolabJS server and local preferences manager)
  • model ---> lib/model (functions that are domain specific and useful across use cases; will be empty for now)
  • service / use cases ---> lib/controllers (view agnostic; the present lib/model code comes here)
  • user interface
    • cli/
      • controller/ (present lib/controller moves here)
      • view/
        • default/ (present lib/cli/input and lib/cli/output move here)
          • input/ (ultimately to be merged as a single view per command)
          • output/
        • fancy/ (new theme with different dependencies)
    • www
      • structure to be decided based on framework chosen

Some suggestions for future:

  • At the moment we are writing the use cases in (lib/model) along the lines of transaction script. For now, we will not build a domain model on the client side.
  • The data source layer uses inheritance to support multiple connection types. At the moment, there is only socket.io connection type to AutolabJS server. We need to build a module following Remote Facade pattern to properly abstract the details of communication protocols.
    One way to achieve this is to create an abstract interface named connect.js in lib/model and have a concrete implementation of the abstract interface to exist in lib/data.
    We can also apply the same ideas to preference-manager.js.
  • All the views shall have their own page controllers which will be put in views module.

Clear answers to our questions on architectural layering will come from reading the book: Eric Evans, Domain Driven Design.

Interface of View

If we carefully, observe init.js, eval.js, exit.js and prefs.js, we see the following patterns.

  • In each controller, there is only one input module and one output module.
  • All input modules export objects with only getInput() function. This function takes different messages in each controller.
  • All output modules export objects with only sendOutput() function. This function takes different messages in each controller.

Thus, we can think of view interface as a package (two modules) with the following interfaces.

//cli/input.js
getInput()
//cli/output.js
sendOutput()

Ideally though, we should have one view interface which has both input and output sections.

Class View {
   getInput()
   sendOutput()
}

Each of the views can implement the View interface and a controller can receive its' view object via constructor.

References

  1. Martin Fowler, anaemic domain models
  2. Service layer illustration
  3. Craig Larman's GRASP pattern
  4. Martin Fowler, Patterns of Enterprise Architecture, 1999. index

Random Ideas

We can also use Inversion of Control frameworks like electrolyte or scatter or intravenous. But, IoC is not a favorite pattern among JS community, especially since JS is a dynamic language.