General Architecture

The application is built with the CQRS architectural pattern and Event-Sourcing as persistence method. The basics will be explained here, but please read up on those two topics, since they are too complex to explain in all depth here.

The application is split in two major parts - the backend part, which provides all the logic and structure for the timing domain, and the frontend part, which is loosely coupled to the backend via a slim API with commands and queries. More on this later.

Folder structure

root
├── /app                        //main application folder
│    ├── /components            //code for the application
│    │    ├── /application                  //additional components
│    │    │    ├── /agegroups               
│    │    │    ├── /certificates
│    │    │    ├── /competition
│    │    │    ├── /event
│    │    │    ├── /lists
│    │    │    ├── /measurements            //abstract detection
│    │    │    ├── /participant             
│    │    │    ├── /participant-import
│    │    │    ├── /participant-settings    //additional datafields for participants
│    │    │    ├── /ranking
│    │    │    ├── /result-management       //definitions of results (in the management tab -> results)
│    │    │    ├── /resultset               //collection of results of a participant
│    │    │    ├── /script                  //scripting language
│    │    │    ├── /taggy                   //connection to mandigo gen2 taggy
│    │    │    ├── /teams                   
│    │    │    ├── /time-processor          //assingment of measurements to a measuring point and additional processors like double-detection time-out
│    │    │    └── /timing                  //overriding of times in the participant
│    │    └── /infrastructure               //the backbone of the application
│    │         ├── /cluster                 //networking
│    │         ├── /commandlog              //creation of commands
│    │         ├── /commandlog-inspector    //search in the commandlog
│    │         ├── /component-manager       //loader for the components
│    │         ├── /core                    //base classes
│    │         ├── /cqrs                    //the main application flow is defined here. See explanation below
│    │         ├── /electron-glue           //connection to the electron framework
│    │         ├── /event-sourced           //state of the application from events
│    │         ├── /event-storage           //saving of the events
│    │         ├── /event-storage-repl      //replication of the event-storage e.g. in network
│    │         ├── /messaging               //base classes for messaging (between components)
│    │         ├── /micro-mock              //framework for testing
│    │         └── /util                    //some helping functionality
│    └── /data                  //db storage
│         ├── /cluster          //network storage
│         ├── /commandlog       //stores all commands
│         ├── /db               //stores the current state of the application
│         └── /eventstore       //stores all events - complete history of the application
├── /dist                       //build folder
├── /node-modules               //installed nodejs modules
├── /static                     //static data for result list layout
├── /test                       //deprecated, not used for anything, can be deleted
├── /ui                         //user interface - aurelia application
│    ├── /ext                   //certificate editor
│    ├── /fonts                 //used fonts
│    ├── /img                   //static images
│    ├── /js                    //js script files for the pages/user interface
│    │    ├── app.js            //connection to the components (JQuery)
│    │    └── ...
│    ├── /jspm_packages         //jspm modules, use of them is deprecated, changed to npm
│    ├── /less                  //stylesheets
│    │    ├── app.less          //loading of the "img" folder
│    │    └── ...
│    ├── /locales               //translations
│    ├── /node_modules          //npm module directory
│    ├── /src                   //source folder for the aurelia framework
│    │    ├── /custom-components//special ui components
│    │    ├── /screens          //screens that the ui consist of
│    │    ├── /valueConverter   //checks if the format is ok
│    │    ├── api-ukuz.js       //interface to the backend application
│    │    └── ...
│    ├── /styles                //compiled css files (from less)
│    ├── config.js              //aurelia config file
│    ├── index.html             //index page for the ui
│    └── ...
├── /docs                       //autogenerated docs - API and full
├── .esdoc.api.json             //settings for API doc-generator 
├── .esdoc.full.json            //settings for full doc-generator
├── .gitignore              
├── .npmrc
├── classMap.json               //cache for component classes loader
├── cluster.js                  //testprogram for networking functionality
├── components.js               //component loader
├── config.js                   //main config file
├── esdoc.class.plugin.js       //doc-generator plugin to help with commonjs module export
├── icon.ico                
├── icon.png
├── LICENSE.md
├── main.js                     //starting point of the application
├── net-chat.js                 //testprogramm for networking functionality
├── package-lock.json           //npm-generated file
├── package.json                //Manages npm settings and packages
├── polyfill.js                 //Workaround for not supported plattforms
├── README.md
└── test.js                     //starting point for the tests

Structure of a component

/component
├── /domain
│    ├── /command               //definition of the commands
│    ├── /events                //definition of the events
│    ├── /model                 //modeling the functionality of the component
│    ├── /query                 //cqrs: local component db; definition how an event can be converted to a db-entry
│    ├── /services              //special service classes
│    └── /process               //automation of processes
├── /test                       //Tests for the functionality of the component
└── package.json                //Needs to specify internal dependencies for correct autoloading

Application flow

┌──────────────┐
│  CommandLog  │    
└──────────────┘
        ^
        │CommandLogger
        │
        │       CommandBus      ┌──────────────────┐                    ┌──────────────────┐
     Command ─────────────────> │  CommandHandler  │ ─────────────────> │    DomainModel   │
        ^                       └──────────────────┘                    └──────────────────┘
        │                              ^                                          │
        │sends                         │ Command                                  │creates  
        │                              │         ┌──────────────────┐             │              ┌──────────────────┐   replication 
┌──────────────┐                        ─────────│  ProcessManager  │<───────── Event ─────────> │    EventStore    │ <─────────────> ...
│      UI      │                                 └──────────────────┘             │              │ (source of Truth)│
└──────────────┘                                                                  │EventBus      └──────────────────┘ 
  data ^│                                                                         │                                        
       ││                   ┌────────────────────────┐                   ┌──────────────────┐
       │└────Query─────────>│       Projection       │<───── Event <─────│   Application    │
       └────────────────────│projects Events in data │                   │(other Components)│
        QueryBus            └────────────────────────┘                   └──────────────────┘
                                        │^
                                        ││
                              ┌────────────────────┐
                              │         DB         │    
                              │current state cache │
                              └────────────────────┘

Basic description

The application is composed of multiple "components" that each fulfill a very specific task and provide a concrete functionality. Each component works on ints own. The communication with other components happens via messages (events, commands and queries). A command is a message that asks the application to do a specific thing, which it may refuse to do, if certain conditions are not met. Commands come from the User or from other Components (via ProcessManagers that automate behaviour). An event is a message that transports the information that something has happened, which can not be undone and the application needs to react accordingly. A query is a message that just asks for data from the application.

Each component registers for specific commands and events by convention of providing CommandHandlers and EventListeners that have handler methods with specific naming ("when" + EventName or "handle" + CommandName). The glueing happens inside the main.js bootstrap. Whenever such an command is sent or an event occurs, the according method of the component class gets invoked. Typically, commands are acted upon by the Domain Model via CommandHandlers and events are acted upon by Projections and ProcessManagers.

Components themself produce events, normally through their Models. When the model is persisted, the Event ends up in the EventStore, which will also emit the Event on the EventBus. That way, whenever an Event is replicated from a different node in the cluster, the application will react as if the Event was created locally. The EventBus will then transport the event to other components, which can in turn react on the event. The UI is connected to the backend via the CommandBus and the QueryBus. There is also a so called "NotificationBus" which only transports messages that a specific Projection has changed to the UI, so that it can send a new query to update the View.