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é.
## Changements principaux
### Domaine Scraping
- 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
### Événement partagé
- ChapterScraped déplacé dans Domain/Shared/Domain/Event/
avec jobId, chapterId, pagesDirectory, pageCount
- Routing Messenger ajouté
### Domaine Manga
- Ajout : ChapterScrapedEventListener + ChapterScrapedMessageHandler
pour mettre à jour Chapter.pagesDirectory via le Repository Manga
### Domaine Reader
- 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
### Architecture
- phparkitect.php : App\Domain\Shared\Domain\Event autorisé dans
les couches Application (correction violations pré-existantes
ChapterImported/VolumeImported + nouvelle ChapterScraped)
## Tests
- 218/218 tests passent (+3 nouveaux)
- InMemoryImageStorage créé pour les tests unitaires
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
640d1cec82
commit
b7f4ee9082
@@ -31,6 +31,8 @@ class InMemoryChapterRepository implements ChapterRepositoryInterface
|
||||
$this->chapters[$chapter->id] = $chapter;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public function clear(): void
|
||||
{
|
||||
$this->chapters = [];
|
||||
|
||||
19
tests/Domain/Scraping/Adapter/InMemoryImageStorage.php
Normal file
19
tests/Domain/Scraping/Adapter/InMemoryImageStorage.php
Normal file
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace App\Tests\Domain\Scraping\Adapter;
|
||||
|
||||
use App\Domain\Scraping\Domain\Contract\Service\ImageStorageInterface;
|
||||
|
||||
class InMemoryImageStorage implements ImageStorageInterface
|
||||
{
|
||||
/** @var array<string, string> chapterId => pagesDirectory */
|
||||
public array $stored = [];
|
||||
|
||||
public function storeChapterImages(string $chapterId, array $localImagePaths): string
|
||||
{
|
||||
$dir = '/fake/pages/' . $chapterId;
|
||||
$this->stored[$chapterId] = $dir;
|
||||
|
||||
return $dir;
|
||||
}
|
||||
}
|
||||
@@ -4,28 +4,26 @@ namespace App\Tests\Domain\Scraping\Application\CommandHandler;
|
||||
|
||||
use App\Domain\Scraping\Application\Command\ScrapeChapter;
|
||||
use App\Domain\Scraping\Application\CommandHandler\ScrapeChapterHandler;
|
||||
use App\Domain\Scraping\Domain\Event\ChapterScraped;
|
||||
use App\Domain\Scraping\Domain\Event\ChapterScrapingFailed;
|
||||
use App\Domain\Scraping\Domain\Event\ChapterScrapingStarted;
|
||||
use App\Domain\Scraping\Domain\Model\Chapter;
|
||||
use App\Domain\Shared\Domain\Model\JobStatus;
|
||||
use App\Domain\Shared\Domain\Event\ChapterScraped;
|
||||
use App\Tests\Domain\Scraping\Adapter\InMemoryChapterRepository;
|
||||
use App\Tests\Domain\Scraping\Adapter\InMemoryCbzGenerator;
|
||||
use App\Tests\Domain\Scraping\Adapter\InMemoryEventBus;
|
||||
use App\Tests\Domain\Scraping\Adapter\InMemoryImageDownloader;
|
||||
use App\Tests\Domain\Scraping\Adapter\InMemoryImageStorage;
|
||||
use App\Tests\Domain\Scraping\Adapter\InMemoryMangaRepository;
|
||||
use App\Tests\Domain\Scraping\Adapter\InMemoryScraperFactory;
|
||||
use App\Tests\Domain\Scraping\Adapter\InMemorySourceRepository;
|
||||
use App\Tests\Domain\Shared\Adapter\InMemoryJobRepository;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class ScrapeChapterHandlerTest extends TestCase
|
||||
{
|
||||
private InMemoryScraperFactory $scraperFactory;
|
||||
private InMemoryImageDownloader $imageDownloader;
|
||||
private InMemoryCbzGenerator $cbzGenerator;
|
||||
private InMemoryImageStorage $imageStorage;
|
||||
private InMemoryJobRepository $jobRepository;
|
||||
private InMemoryChapterRepository $chapterRepository;
|
||||
private InMemoryMangaRepository $mangaRepository;
|
||||
@@ -38,7 +36,7 @@ class ScrapeChapterHandlerTest extends TestCase
|
||||
{
|
||||
$this->scraperFactory = new InMemoryScraperFactory();
|
||||
$this->imageDownloader = new InMemoryImageDownloader();
|
||||
$this->cbzGenerator = new InMemoryCbzGenerator('/test/project/dir');
|
||||
$this->imageStorage = new InMemoryImageStorage();
|
||||
$this->jobRepository = new InMemoryJobRepository();
|
||||
$this->chapterRepository = new InMemoryChapterRepository();
|
||||
$this->mangaRepository = new InMemoryMangaRepository();
|
||||
@@ -55,13 +53,12 @@ class ScrapeChapterHandlerTest extends TestCase
|
||||
mangaId: 'test-manga',
|
||||
chapterNumber: 2,
|
||||
volumeNumber: 1,
|
||||
cbzPath: null,
|
||||
));
|
||||
|
||||
$this->handler = new ScrapeChapterHandler(
|
||||
$this->scraperFactory,
|
||||
$this->imageDownloader,
|
||||
$this->cbzGenerator,
|
||||
$this->imageStorage,
|
||||
$this->jobRepository,
|
||||
$this->chapterRepository,
|
||||
$this->mangaRepository,
|
||||
@@ -87,9 +84,9 @@ class ScrapeChapterHandlerTest extends TestCase
|
||||
$this->assertCount(1, $dispatchedMessages);
|
||||
$this->assertInstanceOf(ChapterScraped::class, $dispatchedMessages[0]);
|
||||
$this->assertEquals($job->id, $dispatchedMessages[0]->getJobId());
|
||||
|
||||
$chapter = $this->chapterRepository->getById('1');
|
||||
$this->assertNotNull($chapter->cbzPath);
|
||||
$this->assertEquals('1', $dispatchedMessages[0]->chapterId);
|
||||
$this->assertEquals('/fake/pages/1', $dispatchedMessages[0]->pagesDirectory);
|
||||
$this->assertNotNull($this->imageStorage->stored['1'] ?? null);
|
||||
}
|
||||
|
||||
protected function tearDown(): void
|
||||
|
||||
Reference in New Issue
Block a user