Skip to content

Patterns

The Control Plane implementation follows a handful of patterns to keep code simple, consistent and maintainable.

Dependency Injection (DI)

(See also Dependency Inversion Principle)

DI helps our code remain agnostic to which cloud provider it runs on, and even how it is assembled and deployed.

With DI, we implement business logic against cloud-agnostic interfaces, then provide implementations of those interfaces at runtime. This allows us to swap out implementations for different cloud providers, or even for local development. We then "build" the app by aggregating one or more collections of "modules" that should be deployed together.

For DI we use the Injector library, which is a Python port of Google Guice. On first glance, it appears to create a lot of boilerplate, but it's doing important work (though we should work to reduce this boilerplate where we can).

Microservices

Microservices provide domain-specific business logic and CRUD operations against a database. Each microservice:

  • Implements an abstract service interface from a corresponding *_api package in the components/apis directory
  • Uses constructor injection with the injector library for all dependencies
  • Defines a bind_*_service() function that binds the implementation as a singleton
  • Has a data layer abstraction in a *DataService class to support different data stores
  • Supports multi-cloud deployment through configuration inheritance (ConfigAWSConfig/GCPConfig)

For the time being, microservices do not implement their own HTTP endpoints to be deployed independently. They are always composed into larger deployment units via dependency injection, such as the BFF components and Monolith.

For detailed instructions on creating a new microservice, see Microservice Pattern.

Code Generation

Code generation ensures that a single source of truth can be defined and reused in different ways.

We currently use code generation to produce the OpenAPI schema based on our FastAPI code.