Skip to content

refactor(usage): collapse the Usage facade into the adapter base, rename it Usage#16

Open
loks0n wants to merge 2 commits into
mainfrom
refactor/drop-usage-facade
Open

refactor(usage): collapse the Usage facade into the adapter base, rename it Usage#16
loks0n wants to merge 2 commits into
mainfrom
refactor/drop-usage-facade

Conversation

@loks0n

@loks0n loks0n commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

Summary

Usage had become a pure pass-through: every method forwarded to the adapter with an identical signature, duplicating the adapter's docblocks verbatim and forcing the adapters to depend on Usage for their own TYPE_* constants (a backwards dependency). The only behaviour it added was a type guard in sum().

This PR removes that layer, then renames the abstract Adapter base — the real contract every backend implements — to Usage, since with the facade gone it is the public entry point: new ClickHouse(...) is a Usage.

End state: one Usage contract, concrete backends under Adapter\ (ClickHouse, Database, SQL), and the Accumulator / Tenant decorators wrapping a Usage.

Changes

  1. Delete the pass-through Usage facade. Accumulator and Tenant wrap the contract directly.
  2. Centralize type validation into a single Usage::assertType(), replacing five duplicated event|gauge guards across Accumulator, ClickHouse, and Database (the ClickHouse batch validator keeps its Metric #N: prefix via the $prefix arg).
  3. TYPE_EVENT / TYPE_GAUGE live on the contract, resolving the old backwards constant dependency.
  4. Rename the abstract base AdapterUsage (src/Usage/Adapter.phpsrc/Usage/Usage.php). Concrete adapters keep the Utopia\Usage\Adapter\ namespace.
  5. getAdapter() is gone — the adapter is the entry point.

No behaviour change: the deleted facade's signatures were identical to the contract's, and step 4 is a pure rename.

Verification

  • composer check (PHPStan) — clean
  • composer lint (Pint) — pass
  • php -l on all changed files — clean
  • Backend-free unit tests (Accumulator, Tenant, Metric, UsageQuery) — 15/15 green

ClickHouse/Database integration tests need a live backend (not run locally); changes there are mechanical (Usage::TYPE_* constants, unwrapping the facade, the rename).

🤖 Generated with Claude Code

loks0n and others added 2 commits June 23, 2026 21:56
Usage was a pure delegating layer: every method just forwarded to the
adapter with an identical signature, duplicating Adapter's docblocks
verbatim and forcing the adapters to depend on Usage for their own
TYPE_* constants (a backwards dependency). The only behaviour it added
was a type guard in sum().

- Delete Usage; Accumulator and Tenant now wrap an Adapter directly.
- Move TYPE_EVENT/TYPE_GAUGE onto Adapter (where they belong) and add a
  single Adapter::assertType() that replaces the 5 duplicated event/gauge
  guards across Accumulator, ClickHouse and Database.
- getAdapter() is gone — the adapter is the entry point.

Net negative LoC, no behaviour change.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
With the pass-through facade gone, the abstract contract every backend
implements is the public entry point — so `Usage` is the right name for
it: `new ClickHouse(...)` is a `Usage`. Concrete backends stay under the
`Adapter\` namespace (ClickHouse, Database, SQL), which now describes
where the implementations live rather than the type callers hold.

- src/Usage/Adapter.php -> src/Usage/Usage.php (abstract class Usage)
- SQL, Accumulator, Tenant and all tests reference Usage / Usage::TYPE_*
- Accumulator/Tenant hold a $usage (was $adapter)

Pure rename, no behaviour change.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@loks0n loks0n changed the title refactor(usage): drop the Usage pass-through facade refactor(usage): collapse the Usage facade into the adapter base, rename it Usage Jun 23, 2026
@greptile-apps

greptile-apps Bot commented Jun 23, 2026

Copy link
Copy Markdown

Greptile Summary

This PR collapses the pass-through Usage facade into the abstract Adapter base and renames that base to Usage, so concrete backends are now the public entry point directly. It also centralizes the event/gauge type guard into a single Usage::assertType() static helper, replacing five duplicated inline checks across Accumulator, ClickHouse, and Database.

  • src/Usage/Adapter.php is deleted; src/Usage/Usage.php is converted from a concrete delegating class to the abstract contract that every backend implements.
  • TYPE_EVENT / TYPE_GAUGE constants and assertType() now live on Usage, eliminating the backwards dependency where adapters imported the facade just for its constants.
  • Accumulator and Tenant accept a Usage (abstract) directly; all test stubs (RecordingAdapter, TenantRecordingAdapter) extend Usage in the same way.

Confidence Score: 4/5

Straightforward structural rename with no logic changes to query paths; the only non-mechanical delta is where assertType() fires in Database::addBatch().

The refactor is mechanically correct and the 15/15 unit tests pass. The one nuance worth watching is that Database::addBatch() now validates $type before entering the skip() closure, so an empty $metrics array paired with an invalid type will throw where it previously returned silently. Database::sum() is also the only method that does not call assertType() at its entry point while its ClickHouse counterpart now does.

src/Usage/Adapter/Database.php — the assertType() placement change in addBatch() and the missing early guard in sum().

Important Files Changed

Filename Overview
src/Usage/Usage.php Old concrete facade class replaced by the abstract base class; adds assertType() static helper and centralizes TYPE_* constants
src/Usage/Adapter.php Deleted — abstract base class responsibilities merged into the renamed Usage.php
src/Usage/Accumulator.php Updated to accept Usage (abstract) directly; type check delegated to Usage::assertType()
src/Usage/Adapter/Database.php Drops Usage facade import; assertType() in addBatch() moved before the skip() closure (minor behavior change for empty metrics); sum() lacks the early assertType() guard that ClickHouse::sum() now has
src/Usage/Adapter/ClickHouse.php All Usage::TYPE_* references updated to self::TYPE_*; assertType() added early in sum(); mechanical and correct
src/Usage/Adapter/SQL.php Parent class changed from Adapter to Usage following the rename — one-line change, correct

Reviews (1): Last reviewed commit: "refactor(usage): rename the Adapter base..." | Re-trigger Greptile

Comment on lines 133 to 140
public function addBatch(array $metrics, string $type, int $batchSize = 1000): bool
{
self::assertType($type);

$this->db->getAuthorization()->skip(function () use ($metrics, $type, $batchSize) {
$entries = [];
foreach ($metrics as $metric) {
if ($type !== Usage::TYPE_EVENT && $type !== Usage::TYPE_GAUGE) {
throw new \InvalidArgumentException("Invalid type '{$type}'. Allowed: event, gauge");
}

if ($metric['value'] < 0) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 The assertType() call moved outside the skip() closure, which subtly changes behavior: when $metrics is an empty array the old per-row guard inside foreach never ran, so addBatch([], 'invalid', 1000) returned without throwing. The new placement throws immediately even for empty batches. That's almost certainly the more correct behavior, but it is a breaking API change for any caller that relied on the old silent-pass for empty metrics.

Suggested change
public function addBatch(array $metrics, string $type, int $batchSize = 1000): bool
{
self::assertType($type);
$this->db->getAuthorization()->skip(function () use ($metrics, $type, $batchSize) {
$entries = [];
foreach ($metrics as $metric) {
if ($type !== Usage::TYPE_EVENT && $type !== Usage::TYPE_GAUGE) {
throw new \InvalidArgumentException("Invalid type '{$type}'. Allowed: event, gauge");
}
if ($metric['value'] < 0) {
public function addBatch(array $metrics, string $type, int $batchSize = 1000): bool
{
self::assertType($type);
if (empty($metrics)) {
return true;
}
$this->db->getAuthorization()->skip(function () use ($metrics, $type, $batchSize) {
$entries = [];
foreach ($metrics as $metric) {
if ($metric['value'] < 0) {

Comment on lines +318 to 321
public function sum(string $tenant, array $queries = [], string $attribute = 'value', string $type = self::TYPE_EVENT): int
{
/** @var array<Metric> $results */
$results = $this->find($tenant, $queries, $type);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Database::sum() is the only method in the adapter hierarchy that does not call assertType() at its entry point — ClickHouse::sum() acquired the early guard in this PR. The type is still validated downstream via withTypeFilter()assertType(), so no invalid value escapes, but the inconsistency makes the defensive contract harder to reason about at a glance.

Suggested change
public function sum(string $tenant, array $queries = [], string $attribute = 'value', string $type = self::TYPE_EVENT): int
{
/** @var array<Metric> $results */
$results = $this->find($tenant, $queries, $type);
public function sum(string $tenant, array $queries = [], string $attribute = 'value', string $type = self::TYPE_EVENT): int
{
self::assertType($type);
/** @var array<Metric> $results */
$results = $this->find($tenant, $queries, $type);

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant