Pular para o conteúdo principal

Dependency Injection & Configuration Container

The project uses the PSR-11 container to manage dependencies. This document covers both the configuration structure and the DI binding patterns that wire everything together.

Configuration Structure

The configuration is organized by environment in the config/{environment}/ folders. Each environment can have:

  • credentials.env - Environment variables (database connections, API keys, JWT secrets, etc.)
  • Numbered PHP files - Dependency injection bindings organized by layer:
    • 01-infrastructure.php - Database, Cache, Logging
    • 02-security.php - JWT, Authentication, CORS
    • 03-api.php - OpenAPI routes, Middleware
    • 04-repositories.php - Repository bindings
    • 05-services.php - Service bindings
    • 06-external.php - External services (Email, etc.)

You must set the APP_ENV environment variable to specify which environment to use.

Example: credentials.env

config/dev/credentials.env
WEB_SERVER=localhost
DASH_SERVER=localhost
WEB_SCHEMA=http
API_SERVER=localhost
API_SCHEMA=http
DBDRIVER_CONNECTION=mysql://root:mysqlp455w0rd@mysql-container/mydb
EMAIL_CONNECTION=smtp://username:[email protected]
JWT_SECRET=OFbOmC2VxlgQHNrBLa/wyj7/fFkgPnLpckbXMVuIU7Sqb3RTztNx3xzEYaoeA31JUpvBjkD7FRKBFGQ0+fnTig==
CORS_SERVERS=.*

Example: 01-infrastructure.php

config/dev/01-infrastructure.php
<?php

use ByJG\Cache\Psr16\BaseCacheEngine;
use ByJG\Cache\Psr16\NoCacheEngine;
use ByJG\Config\DependencyInjection as DI;
use ByJG\Config\Param;

return [
BaseCacheEngine::class => DI::bind(NoCacheEngine::class)->toSingleton(),

DbDriverInterface::class => DI::bind(Factory::class)
->withFactoryMethod("getDbRelationalInstance", [Param::get('DBDRIVER_CONNECTION')])
->toSingleton(),
];

The configuration is loaded by the byjg/config library.

Getting Configuration Values

Use the Config::get() method:

Config::get('WEB_SERVER');

Environment Hierarchy

The available environments are defined in the config/ConfigBootstrap.php file.

The project has four environments with the following inheritance hierarchy:

Inheritance Rules:

  • test inherits from dev
  • staging inherits from dev (with caching enabled)
  • prod inherits from staging and dev (with caching enabled)

Child environments override parent configurations. For example:

  • config/dev/credentials.env defines base database connection
  • config/prod/credentials.env overrides with production database connection
  • config/prod/01-infrastructure.php overrides to use FileSystemCache instead of NoCache

You can modify the environment hierarchy in config/ConfigBootstrap.php:

<?php

use ByJG\Cache\Psr16\FileSystemCacheEngine;
use ByJG\Config\ConfigInitializeInterface;
use ByJG\Config\Definition;
use ByJG\Config\Environment;

return new class implements ConfigInitializeInterface {
public function loadDefinition(?string $env = null): Definition
{
$dev = Environment::create('dev');
$test = Environment::create('test')->inheritFrom($dev);
$staging = Environment::create('staging')->inheritFrom($dev)->withCache(new FileSystemCacheEngine());
$prod = Environment::create('prod')->inheritFrom($staging, $dev)->withCache(new FileSystemCacheEngine());

return (new Definition())
->addEnvironment($dev)
->addEnvironment($test)
->addEnvironment($staging)
->addEnvironment($prod)
->withOSEnvironment([
'TAG_VERSION',
'TAG_COMMIT',
]);
}
};

The Config is automatically initialized when first accessed, thanks to byjg/config's auto-initialization feature.


Dependency Injection Patterns

Dependency Injection (DI) decouples your code from specific implementations, making it easier to swap dependencies based on environment or requirements.

Example: Environment-Specific Cache

You might want caching enabled in production but disabled in development for easier debugging.

Development - config/dev/01-infrastructure.php:

<?php

use ByJG\Cache\Psr16\BaseCacheEngine;
use ByJG\Cache\Psr16\NoCacheEngine;
use ByJG\Config\DependencyInjection as DI;

return [
BaseCacheEngine::class => DI::bind(NoCacheEngine::class)
->toSingleton(),
];

Production - config/prod/01-infrastructure.php:

<?php

use ByJG\Cache\Psr16\BaseCacheEngine;
use ByJG\Cache\Psr16\FileSystemCacheEngine;
use ByJG\Config\DependencyInjection as DI;

return [
BaseCacheEngine::class => DI::bind(FileSystemCacheEngine::class)
->toSingleton(),
];

Usage in Code:

<?php

use ByJG\Config\Config;
use ByJG\Cache\Psr16\BaseCacheEngine;

// Get the cache instance (implementation depends on APP_ENV)
$cache = Config::get(BaseCacheEngine::class);

// Use it the same way regardless of environment
$cache->set('key', 'value', 3600);
$value = $cache->get('key');

The application automatically returns the correct implementation based on the APP_ENV environment variable.

Constructor Injection (withInjectedConstructor)

DummyService::class => DI::bind(DummyService::class)
->withInjectedConstructor()
->toSingleton(),

The container automatically injects dependencies defined in the constructor based on their type hints.

Constructor with Parameters (withConstructorArgs)

JwtWrapper::class => DI::bind(JwtWrapper::class)
->withConstructorArgs([
Param::get('API_SERVER'),
Param::get(JwtKeyInterface::class)
])
->toSingleton(),

Mix environment parameters and other dependencies.

Factory Method (withFactoryMethod)

DbDriverInterface::class => DI::bind(Factory::class)
->withFactoryMethod("getDbRelationalInstance", [
Param::get('DBDRIVER_CONNECTION')
])
->toSingleton(),

Use a factory method instead of a constructor.

Singleton vs Transient (toSingleton)

// Singleton - Same instance every time
MyService::class => DI::bind(MyService::class)->toSingleton(),

// Transient - New instance every time
MyService::class => DI::bind(MyService::class),

Configuration Organization

Dependencies are organized by layer in numbered files:

  • 01-infrastructure.php - Database, Cache, Logging
  • 02-security.php - JWT, Authentication, User Management
  • 03-api.php - OpenAPI, Routes, Middleware
  • 04-repositories.php - Data access layer
  • 05-services.php - Business logic layer
  • 06-external.php - Email, SMS, external APIs

This organization makes it easy to find and modify related configurations.