Skip to content
Download

Injecting Services

Once a service is registered, it can be injected into components that Mach constructs.

Mach uses constructor injection: a component declares its dependencies as constructor parameters, and those dependencies are listed when the component is registered.

Controllers can receive registered services through their constructors.

For example:

class UserService {
public:
std::string getName() const {
return "Alice";
}
};

A controller can depend on UserService:

class UsersController final : public mach::ControllerBase {
public:
inline static constexpr std::string_view route = "/users";
explicit UsersController(UserService& userService)
: m_userService(userService) {}
mach::Reply<std::string> index() {
return ok(m_userService.getName());
}
static void configure(mach::ControllerBuilder<UsersController>& builder) {
builder.mapGet("/", &UsersController::index);
}
private:
UserService& m_userService;
};

Register the service and declare it as a controller dependency:

mach::AppBuilder builder;
builder.addScoped<UserService>();
builder.addController<UsersController, UserService>();
auto app = builder.build();

Mach resolves UserService and passes it to the controller constructor.

Middleware uses the same constructor injection model.

class LoggingMiddleware {
public:
explicit LoggingMiddleware(mach::Logger& logger)
: m_logger(logger) {}
void invoke(mach::Context& context, mach::Next& next) {
m_logger.info("Request received");
next();
}
private:
mach::Logger& m_logger;
};

Declare the dependency when registering the middleware:

mach::AppBuilder builder;
builder.use<LoggingMiddleware, mach::Logger>();
auto app = builder.build();

Mach supplies the dependency when it constructs the middleware.

A component can receive more than one dependency:

class UsersController final : public mach::ControllerBase {
public:
UsersController(UserService& userService, AuditService& auditService)
: m_userService(userService),
m_auditService(auditService) {}
// ...
private:
UserService& m_userService;
AuditService& m_auditService;
};

List the dependencies in the same order when registering the component:

builder.addController<UsersController, UserService, AuditService>();

Injection does not change a service’s lifetime.

When Mach resolves a dependency, the service’s registered lifetime determines which instance is provided:

  • A scoped dependency is reused within its request scope.
  • A singleton dependency is shared across the application.
  • A transient dependency is newly created when resolved.

This means components can depend on services with different lifetimes, provided the lifetime relationship is valid.


You’ve now covered Mach’s dependency injection system: registering services, choosing lifetimes, defining dependencies, and injecting services into application components.