Files
Mangarr/phparkitect.php
ext.jeremy.guillot@maxicoffee.domains c311cfe80c refactor(scraping): DDD refactoring — stockage images individuelles
Le domaine Scraping ne génère plus d'archives CBZ ni ne modifie les
entités du domaine Manga directement. Il scrape, stocke les images
individuellement, et émet un événement partagé.

- Suppression : CbzGeneratorInterface, CbzGenerator, CbzGenerationRequest,
  CbzPath, CbzGenerationException
- Suppression : save() de ChapterRepositoryInterface (Scraping)
- Suppression : cbzPath du modèle Chapter (Scraping)
- Ajout : ImageStorageInterface + LocalImageStorage
  (stockage dans {MANGA_DATA_PATH}/pages/{chapterId}/)
- ScrapeChapterHandler utilise ImageStorage au lieu du générateur CBZ

- ChapterScraped déplacé dans Domain/Shared/Domain/Event/
  avec jobId, chapterId, pagesDirectory, pageCount
- Routing Messenger ajouté

- Ajout : ChapterScrapedEventListener + ChapterScrapedMessageHandler
  pour mettre à jour Chapter.pagesDirectory via le Repository Manga

- LegacyChapterRepository en dual-mode :
  pagesDirectory en priorité, fallback cbzPath (backward compat)
- Requêtes prev/next : filtrent pagesDirectory IS NOT NULL OR cbzPath IS NOT NULL
- ChapterContext expose pagesDirectory

- phparkitect.php : App\Domain\Shared\Domain\Event autorisé dans
  les couches Application (correction violations pré-existantes
  ChapterImported/VolumeImported + nouvelle ChapterScraped)

- 218/218 tests passent (+3 nouveaux)
- InMemoryImageStorage créé pour les tests unitaires

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-09 20:52:16 +01:00

81 lines
3.3 KiB
PHP

<?php
declare(strict_types=1);
use Arkitect\ClassSet;
use Arkitect\CLI\Config;
use Arkitect\Expression\ForClasses\HaveNameMatching;
use Arkitect\Expression\ForClasses\NotDependsOnTheseNamespaces;
use Arkitect\Expression\ForClasses\NotHaveDependencyOutsideNamespace;
use Arkitect\Expression\ForClasses\ResideInOneOfTheseNamespaces;
use Arkitect\Rules\Rule;
return static function (Config $config): void {
$domainClassSet = ClassSet::fromDir(__DIR__ . '/src/Domain');
$businessDomains = ['Manga', 'Reader', 'Scraping', 'Conversion'];
// Classes PHP standards et utilitaires
$standardExceptions = [
'DateTimeImmutable',
'RuntimeException',
'Exception',
'DomainException',
'Symfony\Component\HttpKernel\Exception',
'Throwable',
'InvalidArgumentException',
'App\Domain\Shared\Domain\Model\AggregateRoot',
];
// Dépendances externes autorisées
$externalDependencies = [
'Symfony\Component\Messenger',
'Ramsey\Uuid'
];
// Règle pour le namespace cohérent
$namespaceRule = Rule::allClasses()
->that(new ResideInOneOfTheseNamespaces('App\Domain'))
->should(new HaveNameMatching('*'))
->because('nous voulons un namespace cohérent pour notre domaine');
$rules = [$namespaceRule];
// Règles spécifiques pour le domaine Shared
$rules[] = Rule::allClasses()
->that(new ResideInOneOfTheseNamespaces('App\Domain\Shared'))
->should(new NotHaveDependencyOutsideNamespace(
'App\Domain\Shared',
$standardExceptions
))
->because('le domaine Shared ne doit dépendre de personne');
// Génération des règles pour chaque domaine métier
foreach ($businessDomains as $domain) {
// Règle pour la couche Domain
$rules[] = Rule::allClasses()
->that(new ResideInOneOfTheseNamespaces("App\Domain\\$domain\Domain"))
->should(new NotHaveDependencyOutsideNamespace(
"App\Domain\\$domain\Domain",
$standardExceptions
))
->because("la couche Domain de $domain ne doit dépendre que de ses propres classes, des contrats partagés et des exceptions autorisées");
// Règle pour la couche Application
$rules[] = Rule::allClasses()
->that(new ResideInOneOfTheseNamespaces("App\Domain\\$domain\Application"))
->should(new NotHaveDependencyOutsideNamespace(
"App\Domain\\$domain",
array_merge($standardExceptions, $externalDependencies, ['App\Domain\Shared\Domain\Contract', 'App\Domain\Shared\Domain\Event'])
))
->because("la couche Application de $domain ne peut dépendre que de son propre domaine, des contrats partagés et des dépendances autorisées");
// Interdiction explicite pour l'Application d'accéder à l'Infrastructure
$rules[] = Rule::allClasses()
->that(new ResideInOneOfTheseNamespaces("App\Domain\\$domain\Application"))
->should(new NotDependsOnTheseNamespaces("App\Domain\\$domain\Infrastructure"))
->because("la couche Application de $domain ne doit jamais dépendre de l'Infrastructure, même au sein de son propre domaine");
}
$config->add($domainClassSet, ...$rules);
};