PHPackages                             mgamadeus/ddd-argus - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [HTTP &amp; Networking](/categories/http)
4. /
5. mgamadeus/ddd-argus

ActiveLibrary[HTTP &amp; Networking](/categories/http)

mgamadeus/ddd-argus
===================

External API repository layer for mgamadeus/ddd — batched HTTP calls with multi-tier caching

1.0.8(1mo ago)0148↓33.3%2MITPHP

Since Apr 14Pushed 1mo agoCompare

[ Source](https://github.com/mgamadeus/ddd-argus)[ Packagist](https://packagist.org/packages/mgamadeus/ddd-argus)[ RSS](/packages/mgamadeus-ddd-argus/feed)WikiDiscussions main Synced 1w ago

READMEChangelogDependencies (2)Versions (10)Used By (2)

mgamadeus/ddd-argus
===================

[](#mgamadeusddd-argus)

External API repository layer for the [mgamadeus/ddd](https://github.com/mgamadeus/ddd) framework — batched HTTP calls with multi-tier caching.

Installation
------------

[](#installation)

```
composer require mgamadeus/ddd-argus
```

What it does
------------

[](#what-it-does)

Argus provides a repository pattern for external HTTP APIs, sitting alongside the DB repository layer (`LazyLoadRepo::DB`). It enables:

- **Batched parallel HTTP execution** via Guzzle promises
- **Multi-tier caching** — APC (in-process) + Redis Sentinel (distributed)
- **Bidirectional entity conversion** — domain entities ↔ Argus entities (`fromEntity()` / `toEntity()`)
- **Attribute-driven configuration** — a single `#[ArgusLoad]` attribute per entity defines endpoints, cache level, TTL
- **Full CRUD** — LOAD, CREATE, UPDATE, DELETE, PATCH, SYNCHRONIZE operations
- **Selective property loading** — load only specific child entities on demand

Environment Variables
---------------------

[](#environment-variables)

### Required

[](#required)

```
# Base URL for the Argus batch API gateway
ARGUS_API_ENDPOINT="https://your-api-host.com/api/batch/"
```

### Optional

[](#optional)

```
# JSON override for default HTTP request settings (headers, timeout)
# Only specify keys you want to override — they merge with defaults
ARGUS_REQUEST_SETTINGS='{"headers":{"x-api-key":"your-custom-key"},"timeout":300}'
```

**Default request settings** (built into `ArgusApiOperations::getRequestSettings()`):

```
{
    "headers": {
        "Connection": "Keep-Alive",
        "Keep-Alive": "600",
        "Accept-Charset": "ISO-8859-1,UTF-8;q=0.7,*;q=0.7",
        "Accept-Language": "de,en;q=0.7,en-us;q=0.3",
        "Accept": "*/*",
        "Content-Type": "application/json",
        "x-api-key": "apps-symfony"
    },
    "http_errors": false,
    "timeout": 600
}
```

Usage
-----

[](#usage)

### Creating an Argus repo entity

[](#creating-an-argus-repo-entity)

```
use DDD\Domain\Base\Repo\Argus\ArgusEntity;
use DDD\Domain\Base\Repo\Argus\Attributes\ArgusLoad;
use DDD\Domain\Base\Repo\Argus\Traits\ArgusTrait;
use DDD\Domain\Base\Repo\Argus\Utils\ArgusCache;

#[ArgusLoad(
    loadEndpoint: 'POST:/my-service/endpoint',
    cacheLevel: ArgusCache::CACHELEVEL_MEMORY_AND_DB,
    cacheTtl: ArgusCache::CACHE_TTL_ONE_DAY
)]
class ArgusMyEntity extends MyEntity
{
    use ArgusTrait;

    protected function getLoadPayload(): ?array
    {
        return ['body' => ['param' => $this->someProperty]];
    }

    public function handleLoadResponse(mixed &$callResponseData = null, ?ArgusApiOperation &$apiOperation = null): void
    {
        $data = $this->getResponseDataFromArgusResponse($callResponseData);
        if ($data) {
            $this->resultProperty = $data->value;
        }
        $this->postProcessLoadResponse($callResponseData, $data !== null);
    }
}
```

### Cache levels

[](#cache-levels)

ConstantValueDescription`CACHELEVEL_NONE`0No caching`CACHELEVEL_MEMORY`1APC only (fast, process-local)`CACHELEVEL_DB`2Redis Sentinel only (distributed)`CACHELEVEL_MEMORY_AND_DB`3Both (recommended)### Cache TTL presets

[](#cache-ttl-presets)

ConstantSeconds`CACHE_TTL_TEN_MINUTES`600`CACHE_TTL_THIRTY_MINUTES`1800`CACHE_TTL_ONE_HOUR`3600`CACHE_TTL_ONE_DAY`86400`CACHE_TTL_ONE_WEEK`604800`CACHE_TTL_ONE_MONTH`2292000Authentication
--------------

[](#authentication)

Argus automatically authenticates outgoing API calls using a bearer token. The token is generated for the account specified by:

```
# Account ID used for CLI and Argus batch operations (must have ADMIN or SUPERADMIN role)
CLI_DEFAULT_ACCOUNT_ID_FOR_CLI_OPERATIONS=1
```

The `ArgusApiOperations` class fetches a refresh token for this account via `AuthService`, exchanges it for an access token, and attaches it as `Authorization: Bearer ` to every outgoing request.

Batch endpoints (server-side)
-----------------------------

[](#batch-endpoints-server-side)

Modules that ship batch controllers (ddd-ai, ddd-common-geo) provide the server-side endpoints that Argus clients call. These endpoints **must be secured** in your project's `security.yaml`.

### Security configuration example

[](#security-configuration-example)

```
# config/symfony/default/packages/security.yaml
security:
    password_hashers:
        Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface: 'auto'

    providers:
        app_user_provider:
            id: DDD\Symfony\Security\AccountProviders\AccountProvider
        all_users:
            chain:
                providers: ['app_user_provider']

    firewalls:
        main:
            stateless: true
            provider: all_users
            access_denied_handler: DDD\Symfony\Security\AccessDeniedHandlers\AccessDeniedHandler
            custom_authenticators:
                - DDD\Symfony\Security\Authenticators\TokenAuthenticator
                - DDD\Symfony\Security\Authenticators\LoginTokenAuthenticator

    role_hierarchy:
        ROLE_ADMIN: ROLE_USER
        ROLE_SUPER_ADMIN: [ ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH ]

    access_control:
        - { path: ^/api/public, roles: PUBLIC_ACCESS }
        - { path: ^/api/client, roles: ROLE_USER }
        - { path: ^/api/admin, roles: ROLE_ADMIN }
        - { path: ^/api/batch, roles: ROLE_SUPER_ADMIN }
```

The key line is `{ path: ^/api/batch, roles: ROLE_SUPER_ADMIN }` — this ensures that only the account specified by `CLI_DEFAULT_ACCOUNT_ID_FOR_CLI_OPERATIONS` (which must have SUPER\_ADMIN role) can invoke batch operations. The bearer token is attached automatically by `ArgusApiOperations`.

Architecture
------------

[](#architecture)

### Core concepts

[](#core-concepts)

Argus repo classes follow this pattern:

- Class lives in `Domain//Repo/Argus/...`
- Class **extends** a domain entity, value object, or entity set (not wraps — same properties, extended with loading)
- Class uses `ArgusTrait`
- Class has `#[ArgusLoad(...)]` attribute defining endpoint, cache level, TTL
- Class implements request payload and response parsing methods

### Entity ↔ Repo binding via LazyLoadRepo

[](#entity--repo-binding-via-lazyloadrepo)

For entities loaded through Argus, add the `#[LazyLoadRepo]` attribute on the entity:

```
#[LazyLoadRepo(LazyLoadRepo::ARGUS, ArgusMyEntity::class)]
class MyEntity extends Entity
{
    // When this entity is lazy-loaded with ARGUS repo type,
    // it instantiates ArgusMyEntity and calls argusLoad()
}
```

Keep `uniqueKey()` deterministic for cache stability.

### Bidirectional conversion

[](#bidirectional-conversion)

- `fromEntity(DefaultObject &$entity): static` — copies all public properties from domain entity to Argus entity, recursively converting nested entities to their Argus equivalents
- `toEntity(): DefaultObject|null` — converts back from Argus entity to domain entity after loading

### Loading flow

[](#loading-flow)

```
Entity.lazyLoad()
  → ArgusEntity.argusLoad()
    → argusPrepareLoad()
      → constructApiOperations() recursively for this + children
      → ArgusApiCacheOperations.execute() — batch Redis lookup
      → ArgusApiOperations.execute() — parallel HTTP calls
    → handleLoadResponse() — parse result, populate properties
    → postProcessLoadResponse() — cache result, mark loaded

```

### Selective property loading

[](#selective-property-loading)

Load only specific child entities on demand:

```
$argusEntity->setPropertiesToLoad(
    ArgusChildEntity::class,
    ArgusLoadingParameters::create(ArgusOtherChild::class, 'param1', 'param2')
);
$argusEntity->argusLoad();
```

Or configure properties that always load:

```
ArgusMyEntity::setPropertiesToLoadAlways(ArgusChildEntity::class);
```

### Operation merging

[](#operation-merging)

Multiple similar operations can be merged into a single HTTP call. Set `mergelimit` on the operation payload to control batching (e.g., 10 keyword lookups merged into 1 API call).

Patterns
--------

[](#patterns)

### A) Simple Argus repo (non-LLM)

[](#a-simple-argus-repo-non-llm)

```
#[ArgusLoad(
    loadEndpoint: 'POST:/service/path',
    cacheLevel: ArgusCache::CACHELEVEL_MEMORY_AND_DB,
    cacheTtl: ArgusCache::CACHE_TTL_ONE_DAY
)]
class ArgusMyEntity extends MyEntity
{
    use ArgusTrait;

    protected function getLoadPayload(): ?array
    {
        return ['body' => ['param' => $this->someProperty]];
    }

    public function handleLoadResponse(
        mixed &$callResponseData = null,
        ?ArgusApiOperation &$apiOperation = null
    ): void {
        $data = $this->getResponseDataFromArgusResponse($callResponseData);
        if ($data) {
            $this->resultProperty = $data->value;
        }
        $this->postProcessLoadResponse($callResponseData, $data !== null);
    }

    public function uniqueKey(): string
    {
        return static::uniqueKeyStatic($this->someProperty);
    }
}
```

### B) Argus repo with CRUD operations

[](#b-argus-repo-with-crud-operations)

```
#[ArgusLoad(
    loadEndpoint: 'GET:/resource/{id}',
    createEndpoint: 'POST:/resource',
    updateEndpoint: 'PUT:/resource/{id}',
    deleteEndpoint: 'DELETE:/resource/{id}',
)]
class ArgusResource extends Resource
{
    use ArgusTrait;

    protected function getLoadPayload(): ?array
    {
        return ['path' => ['id' => $this->id], 'body' => []];
    }

    protected function getCreatePayload(): ?array
    {
        return ['body' => ['name' => $this->name]];
    }

    // Path parameters in endpoints like {id} are automatically
    // replaced from the payload's 'path' array
}
```

Usage:

```
$argus = new ArgusResource();
$argus->name = 'New Resource';
$argus->argusCreate();   // POST:/resource
$argus->argusUpdate();   // PUT:/resource/{id}
$argus->argusDelete();   // DELETE:/resource/{id}
```

### C) Multiple load endpoints

[](#c-multiple-load-endpoints)

```
#[ArgusLoad(loadEndpoint: [
    'GET:/metrics/impressions',
    'GET:/metrics/clicks',
])]
class ArgusMetrics extends Metrics
{
    // Each endpoint is called separately, results handled independently
}
```

Or with per-endpoint parameters:

```
#[ArgusLoad(loadEndpoint: [
    ['GET:/metrics/daily' => ['query' => ['type' => 'impressions']]],
    ['GET:/metrics/daily' => ['query' => ['type' => 'clicks']]],
])]
```

### D) AI language model repo

[](#d-ai-language-model-repo)

For LLM-powered repos, use in combination with [ddd-ai](https://github.com/mgamadeus/ddd-ai):

```
#[ArgusLoad(
    loadEndpoint: 'POST:/ai/openRouter/chatCompletions',
    cacheLevel: ArgusCache::CACHELEVEL_MEMORY_AND_DB,
    cacheTtl: ArgusCache::CACHELEVEL_NONE
)]
#[ArgusLanguageModel(
    defaultAIModelName: AIModel::MODEL_OPENAI_GPT5_2,
    defaultAIPromptName: 'My.Custom.Prompt',
)]
class ArgusMyAIEntity extends MyEntity
{
    use ArgusTrait, ArgusAILanguageModelTrait;

    public function getUserContent(): string|array
    {
        return 'The text to process';
        // Or for multimodal (text + images):
        // return [
        //     ['type' => 'text', 'text' => 'Describe this image'],
        //     ['type' => 'image_url', 'image_url' => ['image' => $photoEntity]],
        // ];
    }

    public function getAIPromptWithParametersApplied(): AIPrompt
    {
        $prompt = $this->getAIPrompt();
        $prompt->setParameter('locale', 'de-DE');
        return $prompt;
    }

    protected function applyLoadResult(string $resultText): void
    {
        $this->result = $resultText;
    }
}
```

`ArgusAILanguageModelTrait` handles:

- Model resolution via `AIModel::getService()->getAIModelByName()`
- Prompt resolution via `AIPrompt::getService()->getAIPromptByName()`
- Vendor-specific payload formation (OpenAI / Google Gemini)
- Image part normalization for multimodal payloads
- Response-format mode handling (`DEFAULT`, `JSON_OBJECT`)
- Token counting and cost tracking

Service conventions
-------------------

[](#service-conventions)

- Do **not** cache services in class properties — resolve inline via `Entity::getService()`
- Set `$service->throwErrors = true` before lookups requiring strict failure behavior
- Use `protected` visibility (not `private`) on methods and constants for extensibility

Debugging
---------

[](#debugging)

```
// Deactivate all caching globally
ArgusLoad::$deactivateArgusCache = true;

// Log all API calls and responses
ArgusLoad::$logArgusCalls = true;

// Retrieve logged calls
$calls = ArgusApiOperations::getExecutedArgusCalls();

// Display call payload (echoes JSON and returns)
$argusEntity->argusLoad(displayCall: true);

// Display response (echoes JSON and returns)
$argusEntity->argusLoad(displayResponse: true);
```

Request tracking
----------------

[](#request-tracking)

Every outgoing Argus request includes an `rc-tracking` header with:

- `accountId` — the authenticated account
- `requestUID` — unique ID for the current HTTP/CLI/messenger request (consistent across all operations in one request)
- `calledFrom` — call stack trace showing route/command/handler and method chain

Also provides
-------------

[](#also-provides)

- `BatchRequestDto` / `BatchResponseDto` — base DTOs for batch API controllers (used by other modules)

###  Health Score

40

—

FairBetter than 86% of packages

Maintenance90

Actively maintained with recent releases

Popularity14

Limited adoption so far

Community10

Small or concentrated contributor base

Maturity39

Early-stage or recently created project

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~1 days

Total

9

Last Release

50d ago

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/2896811?v=4)[mgamadeus](/maintainers/mgamadeus)[@mgamadeus](https://github.com/mgamadeus)

---

Top Contributors

[![mgamadeus](https://avatars.githubusercontent.com/u/2896811?v=4)](https://github.com/mgamadeus "mgamadeus (10 commits)")

### Embed Badge

![Health badge](/badges/mgamadeus-ddd-argus/health.svg)

```
[![Health](https://phpackages.com/badges/mgamadeus-ddd-argus/health.svg)](https://phpackages.com/packages/mgamadeus-ddd-argus)
```

###  Alternatives

[aws/aws-sdk-php

AWS SDK for PHP - Use Amazon Web Services in your PHP project

6.3k532.1M2.5k](/packages/aws-aws-sdk-php)[neuron-core/neuron-ai

The PHP Agentic Framework.

1.9k496.1k32](/packages/neuron-core-neuron-ai)[illuminate/http

The Illuminate Http package.

13137.2M6.4k](/packages/illuminate-http)[tencentcloud/tencentcloud-sdk-php

TencentCloudApi php sdk

3751.2M45](/packages/tencentcloud-tencentcloud-sdk-php)[dreamfactory/df-core

DreamFactory(tm) Core Components

1652.0k38](/packages/dreamfactory-df-core)[eslazarev/wildberries-sdk

Wildberries OpenAPI clients (generated).

232.5k](/packages/eslazarev-wildberries-sdk)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
