feat: implement repository architecture with smart caching

- Add base repository interfaces and abstract classes
  - Implement separated read/write repositories for Domain and Username models
  - Add intelligent query caching with automatic invalidation
  - Include cache management service and CLI commands
  - Add comprehensive configuration for cache TTL and monitoring
  - Enhance performance through optimized data access patterns
This commit is contained in:
idevakk
2025-11-15 22:11:19 -08:00
parent ea0bc91251
commit 4615d384be
15 changed files with 1928 additions and 0 deletions

View File

@@ -0,0 +1,317 @@
<?php
namespace App\Repositories\Username\Read;
use App\enum\ProviderType;
use App\enum\UsernameType;
use App\Models\Username;
use App\Repositories\BaseRepository;
use Illuminate\Database\Eloquent\Collection;
class UsernameReadRepository extends BaseRepository
{
protected function getCacheTag(): string
{
return 'usernames';
}
public function getModel(): string
{
return Username::class;
}
public function findActive(): Collection
{
$cacheKey = $this->getCacheKey('active');
return $this->executeQuery(function () {
$result = $this->query
->where('is_active', true)
->where(function ($query) {
$query->whereNull('starts_at')
->orWhere('starts_at', '<=', now());
})
->where(function ($query) {
$query->whereNull('ends_at')
->orWhere('ends_at', '>=', now());
})
->get();
$this->resetQuery();
return $result;
}, $cacheKey, 1800); // 30 minutes
}
public function findActiveByType(?UsernameType $usernameType = null, ?ProviderType $providerType = null): array
{
$params = [
'username_type' => $usernameType?->value,
'provider_type' => $providerType?->value,
];
$cacheKey = $this->getCacheKey('active_by_type', $params);
return $this->executeQuery(function () use ($usernameType, $providerType) {
$query = $this->query
->where('is_active', true)
->where(function ($query) {
$query->whereNull('starts_at')
->orWhere('starts_at', '<=', now());
})
->where(function ($query) {
$query->whereNull('ends_at')
->orWhere('ends_at', '>=', now());
});
if ($usernameType) {
$query->where('username_type', $usernameType->value);
}
if ($providerType) {
$query->where('provider_type', $providerType->value);
}
$result = $query->pluck('username')->toArray();
$this->resetQuery();
return $result;
}, $cacheKey, 1800); // 30 minutes
}
public function findActiveByProvider(ProviderType $providerType): Collection
{
$cacheKey = $this->getCacheKey('active_by_provider', ['provider' => $providerType->value]);
return $this->executeQuery(function () use ($providerType) {
$result = $this->query
->where('is_active', true)
->where('provider_type', $providerType->value)
->where(function ($query) {
$query->whereNull('starts_at')
->orWhere('starts_at', '<=', now());
})
->where(function ($query) {
$query->whereNull('ends_at')
->orWhere('ends_at', '>=', now());
})
->get();
$this->resetQuery();
return $result;
}, $cacheKey, 1800);
}
public function findPublicActive(): Collection
{
$cacheKey = $this->getCacheKey('public_active');
return $this->executeQuery(function () {
$result = $this->query
->where('is_active', true)
->where('username_type', UsernameType::PUBLIC->value)
->where(function ($query) {
$query->whereNull('starts_at')
->orWhere('starts_at', '<=', now());
})
->where(function ($query) {
$query->whereNull('ends_at')
->orWhere('ends_at', '>=', now());
})
->get();
$this->resetQuery();
return $result;
}, $cacheKey, 1800);
}
public function findPremiumActive(): Collection
{
$cacheKey = $this->getCacheKey('premium_active');
return $this->executeQuery(function () {
$result = $this->query
->where('is_active', true)
->where('username_type', UsernameType::PREMIUM->value)
->where(function ($query) {
$query->whereNull('starts_at')
->orWhere('starts_at', '<=', now());
})
->where(function ($query) {
$query->whereNull('ends_at')
->orWhere('ends_at', '>=', now());
})
->get();
$this->resetQuery();
return $result;
}, $cacheKey, 1800);
}
public function findExpiringSoon(int $days = 7): Collection
{
$cacheKey = $this->getCacheKey('expiring_soon', ['days' => $days]);
return $this->executeQuery(function () use ($days) {
$result = $this->query
->where('is_active', true)
->whereNotNull('ends_at')
->where('ends_at', '<=', now()->addDays($days))
->where('ends_at', '>', now())
->orderBy('ends_at', 'asc')
->get();
$this->resetQuery();
return $result;
}, $cacheKey, 900); // 15 minutes
}
public function findInactive(): Collection
{
$cacheKey = $this->getCacheKey('inactive');
return $this->executeQuery(function () {
$result = $this->query
->where('is_active', false)
->orWhere(function ($query) {
$query->whereNotNull('starts_at')
->where('starts_at', '>', now());
})
->orWhere(function ($query) {
$query->whereNotNull('ends_at')
->where('ends_at', '<', now());
})
->get();
$this->resetQuery();
return $result;
}, $cacheKey, 1800);
}
public function findRecentlyUsed(int $hours = 24): Collection
{
$cacheKey = $this->getCacheKey('recently_used', ['hours' => $hours]);
return $this->executeQuery(function () use ($hours) {
$result = $this->query
->whereNotNull('last_used_at')
->where('last_used_at', '>=', now()->subHours($hours))
->orderBy('last_used_at', 'desc')
->get();
$this->resetQuery();
return $result;
}, $cacheKey, 900); // 15 minutes
}
public function findByUsername(string $username): ?Username
{
$cacheKey = $this->getCacheKey('find_by_username', ['username' => $username]);
return $this->executeQuery(function () use ($username) {
$result = $this->query->where('username', $username)->first();
$this->resetQuery();
return $result;
}, $cacheKey, 3600); // 1 hour
}
public function countActive(): int
{
$cacheKey = $this->getCacheKey('count_active');
return $this->executeQuery(function () {
$result = $this->query
->where('is_active', true)
->where(function ($query) {
$query->whereNull('starts_at')
->orWhere('starts_at', '<=', now());
})
->where(function ($query) {
$query->whereNull('ends_at')
->orWhere('ends_at', '>=', now());
})
->count();
$this->resetQuery();
return $result;
}, $cacheKey, 600); // 10 minutes
}
public function countByProvider(): array
{
$cacheKey = $this->getCacheKey('count_by_provider');
return $this->executeQuery(function () {
$result = $this->query
->selectRaw('provider_type, COUNT(*) as count')
->groupBy('provider_type')
->pluck('count', 'provider_type')
->toArray();
$this->resetQuery();
return $result;
}, $cacheKey, 1800);
}
public function countByType(): array
{
$cacheKey = $this->getCacheKey('count_by_type');
return $this->executeQuery(function () {
$result = $this->query
->selectRaw('username_type, COUNT(*) as count')
->groupBy('username_type')
->pluck('count', 'username_type')
->toArray();
$this->resetQuery();
return $result;
}, $cacheKey, 1800);
}
public function findAvailableForUse(UsernameType $type, int $limit = 10): Collection
{
$cacheKey = $this->getCacheKey('available_for_use', [
'type' => $type->value,
'limit' => $limit,
]);
return $this->executeQuery(function () use ($type, $limit) {
$result = $this->query
->where('is_active', true)
->where('username_type', $type->value)
->where(function ($query) {
$query->whereNull('starts_at')
->orWhere('starts_at', '<=', now());
})
->where(function ($query) {
$query->whereNull('ends_at')
->orWhere('ends_at', '>=', now());
})
->orderBy('last_used_at', 'asc')
->limit($limit)
->get();
$this->resetQuery();
return $result;
}, $cacheKey, 300); // 5 minutes
}
public function findUnused(int $days = 30): Collection
{
$cacheKey = $this->getCacheKey('unused', ['days' => $days]);
return $this->executeQuery(function () {
$result = $this->query
->where('is_active', true)
->where(function ($query) {
$query->whereNull('last_used_at')
->orWhere('last_used_at', '<', now()->subDays($days));
})
->orderBy('last_used_at', 'asc')
->get();
$this->resetQuery();
return $result;
}, $cacheKey, 1800);
}
}

View File

@@ -0,0 +1,163 @@
<?php
namespace App\Repositories\Username\Write;
use App\enum\UsernameType;
use App\Models\Username;
use App\Repositories\WriteRepository;
class UsernameWriteRepository extends WriteRepository
{
protected function getCacheTag(): string
{
return 'usernames';
}
protected function getReadRepositoryClass(): string
{
return \App\Repositories\Username\Read\UsernameReadRepository::class;
}
public function activateUsername(Username $username): bool
{
$username->is_active = true;
$result = $username->save();
$this->clearRelatedCache($username);
return $result;
}
public function deactivateUsername(Username $username): bool
{
$username->is_active = false;
$result = $username->save();
$this->clearRelatedCache($username);
return $result;
}
public function updateUsage(Username $username): bool
{
$username->last_used_at = now();
$username->checked_at = now();
$result = $username->save();
$this->clearRelatedCache($username);
return $result;
}
public function updateCheckedAt(Username $username): bool
{
$username->checked_at = now();
$result = $username->save();
$this->clearRelatedCache($username);
return $result;
}
public function setExpiration(Username $username, ?\DateTime $endsAt = null): bool
{
$username->ends_at = $endsAt;
$result = $username->save();
$this->clearRelatedCache($username);
return $result;
}
public function setStartDate(Username $username, ?\DateTime $startsAt = null): bool
{
$username->starts_at = $startsAt;
$result = $username->save();
$this->clearRelatedCache($username);
return $result;
}
public function changeType(Username $username, UsernameType $type): bool
{
$username->username_type = $type->value;
$result = $username->save();
$this->clearRelatedCache($username);
return $result;
}
public function bulkActivate(array $usernameIds): int
{
$updated = Username::whereIn('id', $usernameIds)->update(['is_active' => true]);
$this->clearCache();
return $updated;
}
public function bulkDeactivate(array $usernameIds): int
{
$updated = Username::whereIn('id', $usernameIds)->update(['is_active' => false]);
$this->clearCache();
return $updated;
}
public function updateDailyMailboxLimit(Username $username, int $limit): bool
{
$username->daily_mailbox_limit = $limit;
$result = $username->save();
$this->clearRelatedCache($username);
return $result;
}
public function createWithDefaults(array $data): Username
{
$defaults = [
'is_active' => true,
'daily_mailbox_limit' => 100,
'checked_at' => now(),
];
$usernameData = array_merge($defaults, $data);
return $this->create($usernameData);
}
public function markAsUsed(Username $username): bool
{
$username->last_used_at = now();
$result = $username->save();
$this->clearRelatedCache($username);
return $result;
}
public function resetUsage(Username $username): bool
{
$username->last_used_at = null;
$username->checked_at = now();
$result = $username->save();
$this->clearRelatedCache($username);
return $result;
}
public function bulkUpdateUsage(array $usernameIds): int
{
$updated = Username::whereIn('id', $usernameIds)->update([
'last_used_at' => now(),
'checked_at' => now(),
]);
$this->clearCache();
return $updated;
}
public function bulkResetUsage(array $usernameIds): int
{
$updated = Username::whereIn('id', $usernameIds)->update([
'last_used_at' => null,
'checked_at' => now(),
]);
$this->clearCache();
return $updated;
}
}