omni.services.core extension sits at the base of the micro services framework.
Everything else one might need (transports to interact with the services, configuration, logging, metrics, etc.) are build up as abstractions.
This means the Services framework becomes a set of interchangeable building blocks that allows developers to focus on the service and use these additional building blocks to quickly and efficiently build out and scale it when it is ready without the need to change the service itself.
omni.services.core will dynamically build up an OpenAPI specification so that users of the services have documentation and a contract that they can use to implement the clients of these services.
The services themselves are merely just functions that a user implements. The recommendation here is to build them as standalone functions and not part of a class to avoid one of the main pitfalls of services and that is to store state on the service. Services are then registered with the core and will immediately be available for consumption via the transports that have been activated.
Transports are what allows a user to interact with a service.
This can be as simple as the build in
local:// in-memory transport. Which will merely invoke the service functions or more advanced transports such as the HTTP(S) servers which will set up the service in a more Request/Response-pattern.
Other transports, such as message queue-based ones, would allow to set up a more Pub-Sub type patterns.
These transports are interchangeable without the need to change the service itself allowing a service’s usage pattern to change as it scales. Transports do not know about the services they are using and this means that transports can be enabled at runtime or within an Kit application file and multiple transports can be enabled simultaneously.
As mentioned in the services section, it is important to try and avoid storing state in a service. When multiple instances of a service are running, there is no guarantee that the same instance of a service will be hit again. There are of course use cases where some state does need to be stored in a database for example. For these types of use cases, and again to help developers move quickly and maintain the loosely coupled set up, facilities were added.
Facilities are objects inserted at runtime into the service, as an argument to the service. This allows them to interact with lower level infrastructure. Be it authentication, metrics collection, storing information in a database, etc. These building blocks allow developers to quickly build a mock implementation using in-memory authentication or an in-memory database and as the project advances these components, again without changing the service itself, be replaced with more advanced modules.
Facilities can also be build as extensions allowing them to be shared amongst developers and to be published to the extension registry. This will allow infrastructure teams, handling the metrics platform for example, to provide a facility for all service developers. The metrics team can change and update its facility and publish it so that they have full control over how the metrics are managed and the service developer does not have to worry about changing his code.