Working with components

Components are the basic building blocks of an Asphalt application. They have a narrowly defined set of responsibilities:

  1. Take in configuration through the constructor
  2. Validate the configuration
  3. Add resources to the context (in start())
  4. Close/shut down/clean up resources when the context is torn down (by directly adding a callback on the context with add_teardown_callback(), or by using context_teardown())

The start() method is called either by the parent component or the application runner with a Context as its only argument. The component can use the context to add resources for other components and the application business logic to use. It can also request resources provided by other components to provide some complex service that builds on those resources.

The start() method of a component is only called once, during application startup. When all components have been started, they are disposed of. If any of the components raises an exception, the application startup process fails and any context teardown callbacks scheduled so far are called before the process is exited.

In order to speed up the startup process and to prevent any deadlocks, components should try to add any resources as soon as possible before requesting any. If two or more components end up waiting on each others’ resources, the application will fail to start. Also, if a component needs to perform lengthy operations like connection validation on network clients, it should add all its resources first to avoid the application start timing out.

There is no rule stating that a component cannot add itself to the context as a resource. The reason official Asphalt libraries do not usually do this is that most of them have the option of providing multiple instances of their services, which is obviously not possible when you only add the component itself as a resource.

Hint

It is a good idea to use type hints with typeguard checks (assert check_argument_types()) in the component’s __init__ method to ensure that the received configuration values are of the expected type, but this is of course not required.

Container components

A container component is component that can contain other Asphalt components. The root component of virtually any nontrivial Asphalt application is a container component. Container components can of course contain other container components and so on.

When the container component starts its child components, each start() call is launched in its own task. Therefore all the child components start concurrently and cannot rely on the start order. This is by design. The only way components should be relying on each other is by the sharing of resources in the context.