Code organization

There's API documentation for the types and functions in code. However, the aggregator's code base is quite large and knowing what to look for where is useful. This document describes somewhat higher overview of the code organization into areas of responsibility.

The code is split into multiple Rust crates (eg. packages), each residing in its own directory. These work both as organization units and units of compilation. Some of them might be optional (eg. data sources and internal computations might be turned on or off through Cargo features).

Obviously, there are many ways how to organize code into areas and the current state might not be the best one possible (both subjectively and objectively), but something must have been chosen.

1 Core libraries

These form the foundation and generic parts of aggregator (eg. they are always compiled in).

1.1 libutils

This library holds various utilities of different purposes, but with wide-spread use. Therefore, things like logging and command line parsing lives here.

1.2 libdata

All kinds of small bits and pieces that are widely used live here ‒ data types describing times or statistics, for example. While they have some methods and support implementations (eg. they might know how to be serialized), they are more of building blocks than holding some kind of logic.

1.3 libflow

The library takes the data types from libdata and builds the abstraction of the whole flow from them. It also adds some other related types (eg. the Update that describes addition of information into a flow).

1.4 libquery

This describes the user-facing query and the response. It knows how to deserialize the query and serialize the response. However, it does not know how to perform the query, only how a query looks like.

1.5 libgather

The Gather type that lives here takes care of the live flows and applying updates onto them. This is where data from multiple data sources meet, where the time is split into slices, etc.

1.6 libaggregator

This is the top-level library of the aggregator. It contains the Reactor, which is an event loop that coordinates asynchronous execution of tasks and IOs. It also contains the Keeper, which is responsible for keeping already closed flow slices, running queries on them and (in the future) managing their on-disk storage.

2 Extension interfaces

The dsrc and int-compute libraries hold the interface for the corresponding extensions (data sources and internal computations). These interfaces are used by the core libraries to manipulate the extensions in a generic ways, without any knowledge about the concrete implementations.

3 Extension implementations

The libraries prefixed with dsrc or int-compute hold the actual implementations of the extensions. These are depended on by the main application and plugged into the core Reactor on startup, based on compile-time and run-time configuration.

4 The main application

The top-level crate is the application. It is quite a thin wrapper around libaggregator, which only plugs the movable parts together and starts it up.

5 The test helpers

There are some test-help libraries that are used for comfortable unit test writing. These are not part of the final product.