Entities, Components and The Aggregator
The principial objects in data access are entities and components. Entities are lightweight, iterator-ish objects. Entities are generally created by a single component (the one enumerating the kind of entity), but they can access data from other components as well (those that have data for the kind of entity).
The entities therefore hold reference to a component “aggregator” which works as a thin wrapper around a cluster of components. It basically just provides create-on-demand semantics and hides re-creation details from everyone else.
For the client, right now, mostly everything is accessible under ept::configuration::apt. Specifically, cache::Package (the package entity), cache::Version (version entity), cache::Tag, cache::Facet (debtags).
So now to get a bit more into implementation details. Most of the library is implemented as templates. In itself, this aspect isn’t all too interesting. However, the more interesting bit is, how they are done.
Each component and entity is parametrized by a “configuration”. The configuration is a struct containing a bunch of typedefs, defining which components take which roles.
The aggregator is parametrized as well, and is mostly generic: it will create and provide access to the components specified by the configuration. Therefore, it mostly acts as the run-time counterpart of the compile-time configuration.
Right now, there is no way to omit specific components from the configuration. It would be possible to implement “dummy” components though. Question is, if this is worth the effort. Either way, it’s still possible (and not hard) to swap some components for alternative implementations, and this is mostly the reason for the configuration approach. It both allows us to experiment with new implementations without harming existing code and to provide alternative configurations for different systems (eg, dummy debtags for systems without debtags, alternative implementation of some components for different package system and so on).