diff --git a/install/Application/config/packages/test/akeneo.yaml b/install/Application/config/packages/test/akeneo.yaml
index 415951eb..d0662f9f 100644
--- a/install/Application/config/packages/test/akeneo.yaml
+++ b/install/Application/config/packages/test/akeneo.yaml
@@ -24,7 +24,7 @@ services:
     Synolia\SyliusAkeneoPlugin\Factory\ProductModelPipelineFactory: ~
     Synolia\SyliusAkeneoPlugin\Factory\AssociationTypePipelineFactory: ~
     Synolia\SyliusAkeneoPlugin\Filter\ProductFilter: ~
-    Synolia\SyliusAkeneoPlugin\Provider\AkeneoAttributePropertiesProvider: ~
+    Synolia\SyliusAkeneoPlugin\Provider\Data\AkeneoAttributePropertiesProvider: ~
     Synolia\SyliusAkeneoPlugin\Provider\TaskProvider: ~
     Synolia\SyliusAkeneoPlugin\Retriever\FamilyRetriever: ~
     Synolia\SyliusAkeneoPlugin\Repository\ProductAttributeRepository: ~
diff --git a/src/Builder/Attribute/AssetAttributeValueValueBuilder.php b/src/Builder/Attribute/AssetAttributeValueValueBuilder.php
index be11737e..479d6a33 100644
--- a/src/Builder/Attribute/AssetAttributeValueValueBuilder.php
+++ b/src/Builder/Attribute/AssetAttributeValueValueBuilder.php
@@ -4,7 +4,7 @@
 
 namespace Synolia\SyliusAkeneoPlugin\Builder\Attribute;
 
-use Synolia\SyliusAkeneoPlugin\Provider\AkeneoAttributePropertiesProvider;
+use Synolia\SyliusAkeneoPlugin\Provider\Data\AkeneoAttributePropertiesProvider;
 use Synolia\SyliusAkeneoPlugin\TypeMatcher\Attribute\AssetCollectionAttributeTypeMatcher;
 use Synolia\SyliusAkeneoPlugin\TypeMatcher\Attribute\AttributeTypeMatcher;
 
diff --git a/src/Builder/Attribute/BooleanProductAttributeValueValueBuilder.php b/src/Builder/Attribute/BooleanProductAttributeValueValueBuilder.php
index ddd10346..e776ed3f 100644
--- a/src/Builder/Attribute/BooleanProductAttributeValueValueBuilder.php
+++ b/src/Builder/Attribute/BooleanProductAttributeValueValueBuilder.php
@@ -4,7 +4,7 @@
 
 namespace Synolia\SyliusAkeneoPlugin\Builder\Attribute;
 
-use Synolia\SyliusAkeneoPlugin\Provider\AkeneoAttributePropertiesProvider;
+use Synolia\SyliusAkeneoPlugin\Provider\Data\AkeneoAttributePropertiesProvider;
 use Synolia\SyliusAkeneoPlugin\TypeMatcher\Attribute\AttributeTypeMatcher;
 use Synolia\SyliusAkeneoPlugin\TypeMatcher\Attribute\BooleanAttributeTypeMatcher;
 
diff --git a/src/Builder/Attribute/CollectionReferenceEntityAttributeValueValueBuilder.php b/src/Builder/Attribute/CollectionReferenceEntityAttributeValueValueBuilder.php
index 833517ad..dc3d9455 100644
--- a/src/Builder/Attribute/CollectionReferenceEntityAttributeValueValueBuilder.php
+++ b/src/Builder/Attribute/CollectionReferenceEntityAttributeValueValueBuilder.php
@@ -4,7 +4,7 @@
 
 namespace Synolia\SyliusAkeneoPlugin\Builder\Attribute;
 
-use Synolia\SyliusAkeneoPlugin\Provider\AkeneoAttributePropertiesProvider;
+use Synolia\SyliusAkeneoPlugin\Provider\Data\AkeneoAttributePropertiesProvider;
 use Synolia\SyliusAkeneoPlugin\TypeMatcher\Attribute\AttributeTypeMatcher;
 use Synolia\SyliusAkeneoPlugin\TypeMatcher\Attribute\CollectionReferenceEntityAttributeTypeMatcher;
 
diff --git a/src/Builder/Attribute/DatabaseProductAttributeValueValueBuilder.php b/src/Builder/Attribute/DatabaseProductAttributeValueValueBuilder.php
index 53e5da5a..fb11e315 100644
--- a/src/Builder/Attribute/DatabaseProductAttributeValueValueBuilder.php
+++ b/src/Builder/Attribute/DatabaseProductAttributeValueValueBuilder.php
@@ -4,7 +4,7 @@
 
 namespace Synolia\SyliusAkeneoPlugin\Builder\Attribute;
 
-use Synolia\SyliusAkeneoPlugin\Provider\AkeneoAttributePropertiesProvider;
+use Synolia\SyliusAkeneoPlugin\Provider\Data\AkeneoAttributePropertiesProvider;
 use Synolia\SyliusAkeneoPlugin\TypeMatcher\Attribute\AttributeTypeMatcher;
 use Synolia\SyliusAkeneoPlugin\TypeMatcher\Attribute\DatabaseMappingAttributeTypeMatcher;
 
diff --git a/src/Builder/Attribute/DateProductAttributeValueValueBuilder.php b/src/Builder/Attribute/DateProductAttributeValueValueBuilder.php
index ab8e372e..c46ad860 100644
--- a/src/Builder/Attribute/DateProductAttributeValueValueBuilder.php
+++ b/src/Builder/Attribute/DateProductAttributeValueValueBuilder.php
@@ -7,7 +7,7 @@
 use DateTime;
 use DateTimeInterface;
 use LogicException;
-use Synolia\SyliusAkeneoPlugin\Provider\AkeneoAttributePropertiesProvider;
+use Synolia\SyliusAkeneoPlugin\Provider\Data\AkeneoAttributePropertiesProvider;
 use Synolia\SyliusAkeneoPlugin\TypeMatcher\Attribute\AttributeTypeMatcher;
 use Synolia\SyliusAkeneoPlugin\TypeMatcher\Attribute\DateAttributeTypeMatcher;
 
diff --git a/src/Builder/Attribute/IntegerProductAttributeValueValueBuilder.php b/src/Builder/Attribute/IntegerProductAttributeValueValueBuilder.php
index 9b687afe..7907a1d3 100644
--- a/src/Builder/Attribute/IntegerProductAttributeValueValueBuilder.php
+++ b/src/Builder/Attribute/IntegerProductAttributeValueValueBuilder.php
@@ -4,7 +4,7 @@
 
 namespace Synolia\SyliusAkeneoPlugin\Builder\Attribute;
 
-use Synolia\SyliusAkeneoPlugin\Provider\AkeneoAttributePropertiesProvider;
+use Synolia\SyliusAkeneoPlugin\Provider\Data\AkeneoAttributePropertiesProvider;
 use Synolia\SyliusAkeneoPlugin\TypeMatcher\Attribute\AttributeTypeMatcher;
 use Synolia\SyliusAkeneoPlugin\TypeMatcher\Attribute\IntegerAttributeTypeMatcher;
 
diff --git a/src/Builder/Attribute/MetricProductAttributeValueValueBuilder.php b/src/Builder/Attribute/MetricProductAttributeValueValueBuilder.php
index 1243cad8..4d92fc3b 100644
--- a/src/Builder/Attribute/MetricProductAttributeValueValueBuilder.php
+++ b/src/Builder/Attribute/MetricProductAttributeValueValueBuilder.php
@@ -4,7 +4,7 @@
 
 namespace Synolia\SyliusAkeneoPlugin\Builder\Attribute;
 
-use Synolia\SyliusAkeneoPlugin\Provider\AkeneoAttributePropertiesProvider;
+use Synolia\SyliusAkeneoPlugin\Provider\Data\AkeneoAttributePropertiesProvider;
 use Synolia\SyliusAkeneoPlugin\TypeMatcher\Attribute\AttributeTypeMatcher;
 use Synolia\SyliusAkeneoPlugin\TypeMatcher\Attribute\MetricAttributeTypeMatcher;
 
diff --git a/src/Builder/Attribute/MultiSelectProductAttributeValueValueBuilder.php b/src/Builder/Attribute/MultiSelectProductAttributeValueValueBuilder.php
index 0c60bbb7..41a94320 100644
--- a/src/Builder/Attribute/MultiSelectProductAttributeValueValueBuilder.php
+++ b/src/Builder/Attribute/MultiSelectProductAttributeValueValueBuilder.php
@@ -4,7 +4,7 @@
 
 namespace Synolia\SyliusAkeneoPlugin\Builder\Attribute;
 
-use Synolia\SyliusAkeneoPlugin\Provider\AkeneoAttributePropertiesProvider;
+use Synolia\SyliusAkeneoPlugin\Provider\Data\AkeneoAttributePropertiesProvider;
 use Synolia\SyliusAkeneoPlugin\Transformer\AttributeOptionValueDataTransformerInterface;
 use Synolia\SyliusAkeneoPlugin\TypeMatcher\Attribute\AttributeTypeMatcher;
 use Synolia\SyliusAkeneoPlugin\TypeMatcher\Attribute\MultiSelectAttributeTypeMatcher;
diff --git a/src/Builder/Attribute/ReferenceEntityAttributeValueValueBuilder.php b/src/Builder/Attribute/ReferenceEntityAttributeValueValueBuilder.php
index 0cda57d2..7207750f 100644
--- a/src/Builder/Attribute/ReferenceEntityAttributeValueValueBuilder.php
+++ b/src/Builder/Attribute/ReferenceEntityAttributeValueValueBuilder.php
@@ -11,8 +11,8 @@
 use Synolia\SyliusAkeneoPlugin\Exceptions\Attribute\MissingLocaleTranslationOrScopeException;
 use Synolia\SyliusAkeneoPlugin\Exceptions\Attribute\MissingScopeException;
 use Synolia\SyliusAkeneoPlugin\Exceptions\Attribute\TranslationNotFoundException;
-use Synolia\SyliusAkeneoPlugin\Provider\AkeneoAttributePropertiesProvider;
-use Synolia\SyliusAkeneoPlugin\Provider\AkeneoReferenceEntityAttributeDataProviderInterface;
+use Synolia\SyliusAkeneoPlugin\Provider\Data\AkeneoAttributePropertiesProvider;
+use Synolia\SyliusAkeneoPlugin\Provider\Data\AkeneoReferenceEntityAttributeDataProviderInterface;
 use Synolia\SyliusAkeneoPlugin\Provider\ProductRefEntityAttributeValueValueBuilderProviderInterface;
 use Synolia\SyliusAkeneoPlugin\TypeMatcher\Attribute\AttributeTypeMatcher;
 use Synolia\SyliusAkeneoPlugin\TypeMatcher\Attribute\ReferenceEntityAttributeTypeMatcher;
diff --git a/src/Builder/Attribute/SelectProductAttributeValueValueBuilder.php b/src/Builder/Attribute/SelectProductAttributeValueValueBuilder.php
index 75797ec1..ebc2f267 100644
--- a/src/Builder/Attribute/SelectProductAttributeValueValueBuilder.php
+++ b/src/Builder/Attribute/SelectProductAttributeValueValueBuilder.php
@@ -4,7 +4,7 @@
 
 namespace Synolia\SyliusAkeneoPlugin\Builder\Attribute;
 
-use Synolia\SyliusAkeneoPlugin\Provider\AkeneoAttributePropertiesProvider;
+use Synolia\SyliusAkeneoPlugin\Provider\Data\AkeneoAttributePropertiesProvider;
 use Synolia\SyliusAkeneoPlugin\Transformer\AttributeOptionValueDataTransformerInterface;
 use Synolia\SyliusAkeneoPlugin\TypeMatcher\Attribute\AttributeTypeMatcher;
 use Synolia\SyliusAkeneoPlugin\TypeMatcher\Attribute\SelectAttributeTypeMatcher;
diff --git a/src/Builder/Attribute/TableAttributeValueValueBuilder.php b/src/Builder/Attribute/TableAttributeValueValueBuilder.php
index 2492cc0b..c0b3614b 100644
--- a/src/Builder/Attribute/TableAttributeValueValueBuilder.php
+++ b/src/Builder/Attribute/TableAttributeValueValueBuilder.php
@@ -7,7 +7,7 @@
 use Sylius\Component\Product\Model\ProductAttributeInterface;
 use Sylius\Component\Resource\Repository\RepositoryInterface;
 use Synolia\SyliusAkeneoPlugin\Exceptions\Processor\MissingAkeneoProductAttributeValueProcessorException;
-use Synolia\SyliusAkeneoPlugin\Provider\AkeneoAttributePropertiesProvider;
+use Synolia\SyliusAkeneoPlugin\Provider\Data\AkeneoAttributePropertiesProvider;
 use Synolia\SyliusAkeneoPlugin\Provider\ProductAttributeValue\TableAttributeValueProcessorProviderInterface;
 use Synolia\SyliusAkeneoPlugin\TypeMatcher\Attribute\AttributeTypeMatcher;
 use Synolia\SyliusAkeneoPlugin\TypeMatcher\Attribute\TableAttributeTypeMatcher;
diff --git a/src/Builder/Attribute/TextProductAttributeValueValueBuilder.php b/src/Builder/Attribute/TextProductAttributeValueValueBuilder.php
index 1e003999..f3c5992d 100644
--- a/src/Builder/Attribute/TextProductAttributeValueValueBuilder.php
+++ b/src/Builder/Attribute/TextProductAttributeValueValueBuilder.php
@@ -4,7 +4,7 @@
 
 namespace Synolia\SyliusAkeneoPlugin\Builder\Attribute;
 
-use Synolia\SyliusAkeneoPlugin\Provider\AkeneoAttributePropertiesProvider;
+use Synolia\SyliusAkeneoPlugin\Provider\Data\AkeneoAttributePropertiesProvider;
 use Synolia\SyliusAkeneoPlugin\TypeMatcher\Attribute\AttributeTypeMatcher;
 use Synolia\SyliusAkeneoPlugin\TypeMatcher\Attribute\TextareaAttributeTypeMatcher;
 use Synolia\SyliusAkeneoPlugin\TypeMatcher\Attribute\TextAttributeTypeMatcher;
diff --git a/src/Builder/ProductOptionValue/NonLocalizableOptionValueBuilder.php b/src/Builder/ProductOptionValue/NonLocalizableOptionValueBuilder.php
index afbc8369..f9b256f8 100644
--- a/src/Builder/ProductOptionValue/NonLocalizableOptionValueBuilder.php
+++ b/src/Builder/ProductOptionValue/NonLocalizableOptionValueBuilder.php
@@ -13,7 +13,7 @@
 use Synolia\SyliusAkeneoPlugin\Event\ProductOptionValueTranslation\AfterProcessingProductOptionValueTranslationEvent;
 use Synolia\SyliusAkeneoPlugin\Event\ProductOptionValueTranslation\BeforeProcessingProductOptionValueTranslationEvent;
 use Synolia\SyliusAkeneoPlugin\Exceptions\Builder\ProductOptionValueTranslation\ProductOptionValueTranslationBuilderNotFoundException;
-use Synolia\SyliusAkeneoPlugin\Provider\AkeneoAttributePropertiesProvider;
+use Synolia\SyliusAkeneoPlugin\Provider\Data\AkeneoAttributePropertiesProvider;
 use Synolia\SyliusAkeneoPlugin\Provider\SyliusAkeneoLocaleCodeProvider;
 use Synolia\SyliusAkeneoPlugin\Transformer\ProductOptionValueDataTransformerInterface;
 use Webmozart\Assert\Assert;
diff --git a/src/Builder/ProductOptionValueTranslation/MetricProductOptionValueTranslationBuilder.php b/src/Builder/ProductOptionValueTranslation/MetricProductOptionValueTranslationBuilder.php
index cc00a2af..7fbfe656 100644
--- a/src/Builder/ProductOptionValueTranslation/MetricProductOptionValueTranslationBuilder.php
+++ b/src/Builder/ProductOptionValueTranslation/MetricProductOptionValueTranslationBuilder.php
@@ -14,8 +14,8 @@
 use Synolia\SyliusAkeneoPlugin\Exceptions\Retriever\FamilyMeasureNotFoundException;
 use Synolia\SyliusAkeneoPlugin\Exceptions\Retriever\MeasurableNotFoundException;
 use Synolia\SyliusAkeneoPlugin\Exceptions\UnsupportedAttributeTypeException;
-use Synolia\SyliusAkeneoPlugin\Provider\AkeneoAttributeDataProviderInterface;
-use Synolia\SyliusAkeneoPlugin\Provider\AkeneoAttributePropertiesProvider;
+use Synolia\SyliusAkeneoPlugin\Provider\Data\AkeneoAttributeDataProviderInterface;
+use Synolia\SyliusAkeneoPlugin\Provider\Data\AkeneoAttributePropertiesProvider;
 use Synolia\SyliusAkeneoPlugin\Provider\ProductFilterRulesProviderInterface;
 use Synolia\SyliusAkeneoPlugin\Retriever\FamilyMeasureRetriever;
 use Synolia\SyliusAkeneoPlugin\TypeMatcher\Attribute\AttributeTypeMatcher;
diff --git a/src/Command/AbstractImportCommand.php b/src/Command/AbstractImportCommand.php
index a796c6af..cb53c322 100644
--- a/src/Command/AbstractImportCommand.php
+++ b/src/Command/AbstractImportCommand.php
@@ -43,6 +43,7 @@ protected function configure(): void
             ->addOption('max-concurrency', 'c', InputOption::VALUE_OPTIONAL, 'Max process concurrency', 5)
             ->addOption('batch-after-fetch', 'a', InputOption::VALUE_OPTIONAL, 'Fetch all pages then start processing the batches', true)
             ->addOption('filter', 'f', InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY, 'Add filter')
+            ->addOption('handler', 'i', InputOption::VALUE_OPTIONAL, 'Specify batch handler')
         ;
     }
 
diff --git a/src/Configuration/ConfigurationContextInterface.php b/src/Configuration/ConfigurationContextInterface.php
index cb428371..5e0a062d 100644
--- a/src/Configuration/ConfigurationContextInterface.php
+++ b/src/Configuration/ConfigurationContextInterface.php
@@ -39,4 +39,8 @@ public function disableBatching(): self;
     public function getFilters(): array;
 
     public function setFilters(array $filters): self;
+
+    public function getHandler(): string;
+
+    public function setHandler(string $handler): self;
 }
diff --git a/src/Configuration/ConfigurationContextTrait.php b/src/Configuration/ConfigurationContextTrait.php
index 2587617d..7672eefe 100644
--- a/src/Configuration/ConfigurationContextTrait.php
+++ b/src/Configuration/ConfigurationContextTrait.php
@@ -5,6 +5,7 @@
 namespace Synolia\SyliusAkeneoPlugin\Configuration;
 
 use Symfony\Component\Console\Output\OutputInterface;
+use Synolia\SyliusAkeneoPlugin\Handler\Task\SymfonyProcessTaskHandler;
 
 trait ConfigurationContextTrait
 {
@@ -24,6 +25,8 @@ trait ConfigurationContextTrait
 
     private array $filters = [];
 
+    private string $handler = SymfonyProcessTaskHandler::HANDLER_CODE;
+
     public function getBatchSize(): int
     {
         return $this->batchSize;
@@ -132,4 +135,16 @@ public function setFilters(array $filters): ConfigurationContextInterface
 
         return $this;
     }
+
+    public function getHandler(): string
+    {
+        return $this->handler;
+    }
+
+    public function setHandler(string $handler): ConfigurationContextInterface
+    {
+        $this->handler = $handler;
+
+        return $this;
+    }
 }
diff --git a/src/Entity/Asset.php b/src/Entity/Asset.php
index d0c340f5..81c1f93d 100644
--- a/src/Entity/Asset.php
+++ b/src/Entity/Asset.php
@@ -22,6 +22,7 @@
  */
 #[ORM\Entity(repositoryClass: AssetRepository::class)]
 #[ORM\Table(name: 'akeneo_assets')]
+#[ORM\Index(columns: ['family_code', 'asset_code', 'attribute_code'], name: 'asset_idx')]
 class Asset implements AssetInterface
 {
     /**
diff --git a/src/Exceptions/NotImplementedException.php b/src/Exceptions/NotImplementedException.php
new file mode 100644
index 00000000..fdd48440
--- /dev/null
+++ b/src/Exceptions/NotImplementedException.php
@@ -0,0 +1,9 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Synolia\SyliusAkeneoPlugin\Exceptions;
+
+class NotImplementedException extends \LogicException
+{
+}
diff --git a/src/Factory/Message/Batch/BatchMessageFactory.php b/src/Factory/Message/Batch/BatchMessageFactory.php
new file mode 100644
index 00000000..e4e590b8
--- /dev/null
+++ b/src/Factory/Message/Batch/BatchMessageFactory.php
@@ -0,0 +1,16 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Synolia\SyliusAkeneoPlugin\Factory\Message\Batch;
+
+use Synolia\SyliusAkeneoPlugin\Message\Batch\BatchMessageInterface;
+use Synolia\SyliusAkeneoPlugin\Payload\PipelinePayloadInterface;
+
+class BatchMessageFactory implements BatchMessageFactoryInterface
+{
+    public static function createFromPayload(PipelinePayloadInterface $payload, array $items): BatchMessageInterface
+    {
+        return $payload->createBatchMessage($items);
+    }
+}
diff --git a/src/Factory/Message/Batch/BatchMessageFactoryInterface.php b/src/Factory/Message/Batch/BatchMessageFactoryInterface.php
new file mode 100644
index 00000000..65586634
--- /dev/null
+++ b/src/Factory/Message/Batch/BatchMessageFactoryInterface.php
@@ -0,0 +1,13 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Synolia\SyliusAkeneoPlugin\Factory\Message\Batch;
+
+use Synolia\SyliusAkeneoPlugin\Message\Batch\BatchMessageInterface;
+use Synolia\SyliusAkeneoPlugin\Payload\PipelinePayloadInterface;
+
+interface BatchMessageFactoryInterface
+{
+    public static function createFromPayload(PipelinePayloadInterface $payload, array $items): BatchMessageInterface;
+}
diff --git a/src/Factory/PayloadFactory.php b/src/Factory/PayloadFactory.php
index c1297fdc..eefdd8a0 100644
--- a/src/Factory/PayloadFactory.php
+++ b/src/Factory/PayloadFactory.php
@@ -53,6 +53,7 @@ private function createContext(
             ->setBatchSize((int) $input->getOption('batch-size'))
             ->setMaxRunningProcessQueueSize((int) $input->getOption('max-concurrency'))
             ->setFilters((array) ($input->getOption('filter') ?: []))
+            ->setHandler($input->getOption('handler') ?? $context->getHandler())
         ;
 
         if (!$isBatchingAllowed) {
diff --git a/src/Factory/ProductPipelineFactory.php b/src/Factory/ProductPipelineFactory.php
index 73289b1e..4561cbf9 100644
--- a/src/Factory/ProductPipelineFactory.php
+++ b/src/Factory/ProductPipelineFactory.php
@@ -8,8 +8,8 @@
 use League\Pipeline\PipelineInterface;
 use Synolia\SyliusAkeneoPlugin\Pipeline\Processor;
 use Synolia\SyliusAkeneoPlugin\Task\Product\ProcessProductsTask;
-use Synolia\SyliusAkeneoPlugin\Task\Product\SetupProductTask;
-use Synolia\SyliusAkeneoPlugin\Task\Product\TearDownProductTask;
+use Synolia\SyliusAkeneoPlugin\Task\SetupTask;
+use Synolia\SyliusAkeneoPlugin\Task\TearDownTask;
 
 final class ProductPipelineFactory extends AbstractPipelineFactory
 {
@@ -18,9 +18,9 @@ public function create(): PipelineInterface
         $pipeline = new Pipeline(new Processor($this->dispatcher));
 
         return $pipeline
-            ->pipe($this->taskProvider->get(SetupProductTask::class))
+            ->pipe($this->taskProvider->get(SetupTask::class))
             ->pipe($this->taskProvider->get(ProcessProductsTask::class))
-            ->pipe($this->taskProvider->get(TearDownProductTask::class))
+            ->pipe($this->taskProvider->get(TearDownTask::class))
         ;
     }
 }
diff --git a/src/Handler/Task/SymfonyMessengerTaskHandler.php b/src/Handler/Task/SymfonyMessengerTaskHandler.php
new file mode 100644
index 00000000..d3678d64
--- /dev/null
+++ b/src/Handler/Task/SymfonyMessengerTaskHandler.php
@@ -0,0 +1,121 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Synolia\SyliusAkeneoPlugin\Handler\Task;
+
+use Akeneo\Pim\ApiClient\Pagination\Page;
+use Akeneo\Pim\ApiClient\Pagination\PageInterface;
+use Akeneo\Pim\ApiClient\Pagination\ResourceCursorInterface;
+use Doctrine\ORM\EntityManagerInterface;
+use Psr\Log\LoggerInterface;
+use Symfony\Component\Messenger\MessageBusInterface;
+use Synolia\SyliusAkeneoPlugin\Factory\Message\Batch\BatchMessageFactoryInterface;
+use Synolia\SyliusAkeneoPlugin\Payload\PipelinePayloadInterface;
+
+class SymfonyMessengerTaskHandler implements TaskHandlerInterface
+{
+    public const HANDLER_CODE = 'messenger';
+
+    public function __construct(
+        protected EntityManagerInterface $entityManager,
+        protected LoggerInterface $logger,
+        private MessageBusInterface $bus,
+        private BatchMessageFactoryInterface $batchMessageFactory,
+    ) {
+    }
+
+    public function support(PipelinePayloadInterface $pipelinePayload): bool
+    {
+        return $pipelinePayload->getCommandContext()->getHandler() === self::HANDLER_CODE;
+    }
+
+    public function batch(PipelinePayloadInterface $pipelinePayload, array $items): void
+    {
+        $this->bus->dispatch($this->batchMessageFactory->createFromPayload($pipelinePayload, $items));
+    }
+
+    public function process(PipelinePayloadInterface $pipelinePayload): void
+    {
+    }
+
+    public function handle(
+        PipelinePayloadInterface $pipelinePayload,
+        ResourceCursorInterface|PageInterface $handleType,
+    ): void {
+        $count = 0;
+        $items = [];
+
+        if ($handleType instanceof PageInterface) {
+            $this->handleByPage($pipelinePayload, $handleType, $count, $items);
+        } else {
+            $this->handleByCursor($pipelinePayload, $handleType, $count, $items);
+        }
+    }
+
+    private function handleByPage(
+        PipelinePayloadInterface $payload,
+        PageInterface $page,
+        int &$count = 0,
+        array &$items = [],
+    ): void {
+        while (
+            ($page instanceof Page && $page->hasNextPage()) ||
+            ($page instanceof Page && !$page->hasPreviousPage()) ||
+            $page instanceof Page
+        ) {
+            foreach ($page->getItems() as $item) {
+                ++$count;
+                $items[] = $item;
+                $identifiers[] = $item['code'] ?? $item['identifier'];
+
+                if (0 === $count % $payload->getBatchSize()) {
+                    $this->logger->notice('Batching', ['codes' => $identifiers]);
+                    $this->batch($payload, $items);
+                    $items = [];
+                }
+            }
+
+            $page = $page->getNextPage();
+        }
+
+        if ($items !== []) {
+            $this->batch($payload, $items);
+            $items = [];
+        }
+    }
+
+    private function handleByCursor(
+        PipelinePayloadInterface $payload,
+        ResourceCursorInterface $resourceCursor,
+        int &$count = 0,
+        array &$items = [],
+    ): void {
+        foreach ($resourceCursor as $item) {
+            ++$count;
+            $items[] = $item;
+            $identifiers[] = $item['code'];
+
+            if (0 === $count % $payload->getBatchSize()) {
+                $this->logger->notice('Batching', ['codes' => $identifiers]);
+                $this->batch($payload, $items);
+                $items = [];
+            }
+        }
+
+        if ($items !== []) {
+            $this->batch($payload, $items);
+            $items = [];
+        }
+    }
+
+    public function setUp(PipelinePayloadInterface $pipelinePayload): PipelinePayloadInterface
+    {
+        return $pipelinePayload;
+    }
+
+    public function tearDown(PipelinePayloadInterface $pipelinePayload): PipelinePayloadInterface
+    {
+        return $pipelinePayload;
+    }
+}
diff --git a/src/Task/AbstractProcessTask.php b/src/Handler/Task/SymfonyProcessTaskHandler.php
similarity index 58%
rename from src/Task/AbstractProcessTask.php
rename to src/Handler/Task/SymfonyProcessTaskHandler.php
index 9685529c..54250df3 100644
--- a/src/Task/AbstractProcessTask.php
+++ b/src/Handler/Task/SymfonyProcessTaskHandler.php
@@ -2,11 +2,12 @@
 
 declare(strict_types=1);
 
-namespace Synolia\SyliusAkeneoPlugin\Task;
+namespace Synolia\SyliusAkeneoPlugin\Handler\Task;
 
 use Akeneo\Pim\ApiClient\Pagination\Page;
 use Akeneo\Pim\ApiClient\Pagination\PageInterface;
 use Akeneo\Pim\ApiClient\Pagination\ResourceCursorInterface;
+use Doctrine\DBAL\Exception;
 use Doctrine\DBAL\ParameterType;
 use Doctrine\DBAL\Result;
 use Doctrine\DBAL\Statement;
@@ -16,10 +17,14 @@
 use Synolia\SyliusAkeneoPlugin\Logger\Messages;
 use Synolia\SyliusAkeneoPlugin\Manager\ProcessManagerInterface;
 use Synolia\SyliusAkeneoPlugin\Payload\PipelinePayloadInterface;
+use Synolia\SyliusAkeneoPlugin\Provider\Task\Batch\BatchTaskProvider;
+use Synolia\SyliusAkeneoPlugin\Provider\Task\Batch\Payload\PayloadBatchTaskProvider;
 use Throwable;
 
-abstract class AbstractProcessTask implements AkeneoTaskInterface
+class SymfonyProcessTaskHandler implements TaskHandlerInterface
 {
+    public const HANDLER_CODE = 'process';
+
     private int $updateCount = 0;
 
     private int $createCount = 0;
@@ -29,12 +34,18 @@ abstract class AbstractProcessTask implements AkeneoTaskInterface
     public function __construct(
         protected EntityManagerInterface $entityManager,
         protected ProcessManagerInterface $processManager,
-        protected BatchTaskInterface $task,
         protected LoggerInterface $logger,
         private string $projectDir,
+        private PayloadBatchTaskProvider $payloadBatchTaskProvider,
+        private BatchTaskProvider $batchTaskProvider,
     ) {
     }
 
+    public function support(PipelinePayloadInterface $pipelinePayload): bool
+    {
+        return $pipelinePayload->getCommandContext()->getHandler() === self::HANDLER_CODE;
+    }
+
     protected function count(string $tableName): int
     {
         /** @var Result $query */
@@ -76,20 +87,20 @@ protected function prepareSelectBatchIdsQuery(
         return $query;
     }
 
-    protected function batch(
-        PipelinePayloadInterface $payload,
+    public function batch(
+        PipelinePayloadInterface $pipelinePayload,
         array $ids,
     ): void {
-        if ($payload->allowParallel()) {
+        if ($pipelinePayload->allowParallel()) {
             $processArguments = [
                 'php',
                 'bin/console',
-                $payload->getCommandName(),
+                $pipelinePayload->getCommandName(),
                 implode(',', $ids),
             ];
 
-            if ('' !== $payload->getVerbosityArgument()) {
-                $processArguments[] = $payload->getVerbosityArgument();
+            if ('' !== $pipelinePayload->getVerbosityArgument()) {
+                $processArguments[] = $pipelinePayload->getVerbosityArgument();
             }
 
             $process = new Process($processArguments, $this->projectDir);
@@ -105,31 +116,33 @@ protected function batch(
             return;
         }
 
-        $batchPayload = $this->createBatchPayload($payload);
+        $batchPayload = $this->payloadBatchTaskProvider->createBatchPayload($pipelinePayload);
         $batchPayload->setIds($ids);
-        $this->task->__invoke($batchPayload);
+        $this->batchTaskProvider->getTask($pipelinePayload)->__invoke($batchPayload);
     }
 
-    abstract protected function createBatchPayload(PipelinePayloadInterface $payload): PipelinePayloadInterface;
-
-    protected function process(PipelinePayloadInterface $initialPayload): void
+    /**
+     * @throws Throwable
+     * @throws Exception
+     */
+    public function process(PipelinePayloadInterface $pipelinePayload): void
     {
-        $this->processManager->setInstantProcessing($initialPayload->getProcessAsSoonAsPossible());
-        $this->processManager->setNumberOfParallelProcesses($initialPayload->getMaxRunningProcessQueueSize());
+        $this->processManager->setInstantProcessing($pipelinePayload->getProcessAsSoonAsPossible());
+        $this->processManager->setNumberOfParallelProcesses($pipelinePayload->getMaxRunningProcessQueueSize());
 
         $this->logger->debug(self::class);
-        $this->type = $initialPayload->getType();
+        $this->type = $pipelinePayload->getType();
         $this->logger->notice(Messages::createOrUpdate($this->type));
 
         try {
-            $totalItemsCount = $this->count($initialPayload->getTmpTableName());
+            $totalItemsCount = $this->count($pipelinePayload->getTmpTableName());
 
             if (0 === $totalItemsCount) {
                 return;
             }
 
-            $min = $this->min($initialPayload->getTmpTableName());
-            $query = $this->prepareSelectBatchIdsQuery($initialPayload->getTmpTableName(), $min - 1, $initialPayload->getBatchSize());
+            $min = $this->min($pipelinePayload->getTmpTableName());
+            $query = $this->prepareSelectBatchIdsQuery($pipelinePayload->getTmpTableName(), $min - 1, $pipelinePayload->getBatchSize());
             /** @var Result $queryResult */
             $queryResult = $query->executeQuery();
 
@@ -139,9 +152,9 @@ protected function process(PipelinePayloadInterface $initialPayload): void
                     $ids[] = $result['id'];
                 }
 
-                $this->batch($initialPayload, $ids);
+                $this->batch($pipelinePayload, $ids);
 
-                $query = $this->prepareSelectBatchIdsQuery($initialPayload->getTmpTableName(), (int) $result['id'], $initialPayload->getBatchSize());
+                $query = $this->prepareSelectBatchIdsQuery($pipelinePayload->getTmpTableName(), (int) $result['id'], $pipelinePayload->getBatchSize());
                 $queryResult = $query->executeQuery();
             }
 
@@ -161,46 +174,48 @@ protected function process(PipelinePayloadInterface $initialPayload): void
      *
      * @TODO Probably need to be refactored
      */
-    protected function handle(
-        PipelinePayloadInterface $payload,
-        \Akeneo\Pim\ApiClient\Pagination\ResourceCursorInterface|\Akeneo\Pim\ApiClient\Pagination\PageInterface $handleType,
+    public function handle(
+        PipelinePayloadInterface $pipelinePayload,
+        ResourceCursorInterface|PageInterface $handleType,
     ): void {
-        $this->processManager->setInstantProcessing($payload->getProcessAsSoonAsPossible());
-        $this->processManager->setNumberOfParallelProcesses($payload->getMaxRunningProcessQueueSize());
+        $this->processManager->setInstantProcessing($pipelinePayload->getProcessAsSoonAsPossible());
+        $this->processManager->setNumberOfParallelProcesses($pipelinePayload->getMaxRunningProcessQueueSize());
 
         $count = 0;
         $ids = [];
 
         if ($handleType instanceof PageInterface) {
-            $this->handleByPage($payload, $handleType, $count, $ids);
+            $this->handleByPage($pipelinePayload, $handleType, $count, $ids);
         } else {
-            $this->handleByCursor($payload, $handleType, $count, $ids);
+            $this->handleByCursor($pipelinePayload, $handleType, $count, $ids);
         }
 
-        if ($count > 0 && count($ids) > 0 && $payload->isBatchingAllowed() && $payload->getProcessAsSoonAsPossible() && $payload->allowParallel()) {
+        if ($count > 0 && count($ids) > 0 && $pipelinePayload->isBatchingAllowed() && $pipelinePayload->getProcessAsSoonAsPossible() && $pipelinePayload->allowParallel()) {
             $this->logger->notice('Batching', ['from_id' => $ids[0], 'to_id' => $ids[(is_countable($ids) ? \count($ids) : 0) - 1]]);
-            $this->batch($payload, $ids);
+            $this->batch($pipelinePayload, $ids);
 
             return;
         }
 
-        if ($count > 0 && count($ids) > 0 && $payload->isBatchingAllowed() && $payload->getProcessAsSoonAsPossible() && !$payload->allowParallel()) {
-            $payload->setIds($ids);
-            $this->task->__invoke($payload);
+        if ($count > 0 && count($ids) > 0 && $pipelinePayload->isBatchingAllowed() && $pipelinePayload->getProcessAsSoonAsPossible() && !$pipelinePayload->allowParallel()) {
+            $pipelinePayload->setIds($ids);
+            $this->batchTaskProvider->getTask($pipelinePayload)->__invoke($pipelinePayload);
 
             return;
         }
 
-        if ($count > 0 && count($ids) > 0 && !$payload->isBatchingAllowed()) {
-            $payload->setIds($ids);
-            $this->task->__invoke($payload);
+        if ($count > 0 && count($ids) > 0 && !$pipelinePayload->isBatchingAllowed()) {
+            $pipelinePayload->setIds($ids);
+            $this->batchTaskProvider->getTask($pipelinePayload)->__invoke($pipelinePayload);
 
             return;
         }
 
-        if ($count > 0 && !$payload->getProcessAsSoonAsPossible()) {
-            $this->process($payload);
+        if ($count > 0 && !$pipelinePayload->getProcessAsSoonAsPossible()) {
+            $this->process($pipelinePayload);
         }
+
+        $this->processManager->waitForAllProcesses();
     }
 
     private function handleByPage(
@@ -266,4 +281,40 @@ private function handleByCursor(
             }
         }
     }
+
+    public function setUp(PipelinePayloadInterface $pipelinePayload): PipelinePayloadInterface
+    {
+        if ($pipelinePayload->isContinue()) {
+            $schemaManager = $this->entityManager->getConnection()->getSchemaManager();
+            $tableExist = $schemaManager->tablesExist([$pipelinePayload->getTmpTableName()]);
+
+            if (true === $tableExist) {
+                return $pipelinePayload;
+            }
+        }
+
+        $this->tearDown($pipelinePayload);
+
+        $query = sprintf(
+            'CREATE TABLE `%s` (
+              `id` INT NOT NULL AUTO_INCREMENT,
+              `values` JSON NULL,
+              PRIMARY KEY (`id`));',
+            $pipelinePayload->getTmpTableName(),
+        );
+        $this->entityManager->getConnection()->executeStatement($query);
+
+        return $pipelinePayload;
+    }
+
+    public function tearDown(PipelinePayloadInterface $pipelinePayload): PipelinePayloadInterface
+    {
+        $exists = $this->entityManager->getConnection()->getSchemaManager()->tablesExist([$pipelinePayload->getTmpTableName()]);
+
+        if ($exists) {
+            $this->entityManager->getConnection()->getSchemaManager()->dropTable($pipelinePayload->getTmpTableName());
+        }
+
+        return $pipelinePayload;
+    }
 }
diff --git a/src/Handler/Task/TaskHandlerInterface.php b/src/Handler/Task/TaskHandlerInterface.php
new file mode 100644
index 00000000..8e959a3b
--- /dev/null
+++ b/src/Handler/Task/TaskHandlerInterface.php
@@ -0,0 +1,32 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Synolia\SyliusAkeneoPlugin\Handler\Task;
+
+use Akeneo\Pim\ApiClient\Pagination\PageInterface;
+use Akeneo\Pim\ApiClient\Pagination\ResourceCursorInterface;
+use Symfony\Component\DependencyInjection\Attribute\AutoconfigureTag;
+use Synolia\SyliusAkeneoPlugin\Payload\PipelinePayloadInterface;
+
+#[AutoconfigureTag()]
+interface TaskHandlerInterface
+{
+    public function support(PipelinePayloadInterface $pipelinePayload): bool;
+
+    public function setUp(PipelinePayloadInterface $pipelinePayload): PipelinePayloadInterface;
+
+    public function tearDown(PipelinePayloadInterface $pipelinePayload): PipelinePayloadInterface;
+
+    public function batch(
+        PipelinePayloadInterface $pipelinePayload,
+        array $items,
+    ): void;
+
+    public function process(PipelinePayloadInterface $pipelinePayload): void;
+
+    public function handle(
+        PipelinePayloadInterface $pipelinePayload,
+        ResourceCursorInterface|PageInterface $handleType,
+    ): void;
+}
diff --git a/src/Message/Batch/AssociationTypeBatchMessage.php b/src/Message/Batch/AssociationTypeBatchMessage.php
new file mode 100644
index 00000000..b00fb441
--- /dev/null
+++ b/src/Message/Batch/AssociationTypeBatchMessage.php
@@ -0,0 +1,12 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Synolia\SyliusAkeneoPlugin\Message\Batch;
+
+class AssociationTypeBatchMessage implements BatchMessageInterface
+{
+    public function __construct(public array $items)
+    {
+    }
+}
diff --git a/src/Message/Batch/AttributeBatchMessage.php b/src/Message/Batch/AttributeBatchMessage.php
new file mode 100644
index 00000000..e59570b3
--- /dev/null
+++ b/src/Message/Batch/AttributeBatchMessage.php
@@ -0,0 +1,12 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Synolia\SyliusAkeneoPlugin\Message\Batch;
+
+class AttributeBatchMessage implements BatchMessageInterface
+{
+    public function __construct(public array $items)
+    {
+    }
+}
diff --git a/src/Message/Batch/BatchMessageInterface.php b/src/Message/Batch/BatchMessageInterface.php
new file mode 100644
index 00000000..4d20c76b
--- /dev/null
+++ b/src/Message/Batch/BatchMessageInterface.php
@@ -0,0 +1,9 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Synolia\SyliusAkeneoPlugin\Message\Batch;
+
+interface BatchMessageInterface
+{
+}
diff --git a/src/Message/Batch/ProductBatchMessage.php b/src/Message/Batch/ProductBatchMessage.php
new file mode 100644
index 00000000..081476db
--- /dev/null
+++ b/src/Message/Batch/ProductBatchMessage.php
@@ -0,0 +1,12 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Synolia\SyliusAkeneoPlugin\Message\Batch;
+
+class ProductBatchMessage implements BatchMessageInterface
+{
+    public function __construct(public array $items)
+    {
+    }
+}
diff --git a/src/Message/Batch/ProductVariantBatchMessage.php b/src/Message/Batch/ProductVariantBatchMessage.php
new file mode 100644
index 00000000..72294fbe
--- /dev/null
+++ b/src/Message/Batch/ProductVariantBatchMessage.php
@@ -0,0 +1,12 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Synolia\SyliusAkeneoPlugin\Message\Batch;
+
+class ProductVariantBatchMessage implements BatchMessageInterface
+{
+    public function __construct(public array $items)
+    {
+    }
+}
diff --git a/src/MessageHandler/Batch/AssociationTypeBatchMessageHandler.php b/src/MessageHandler/Batch/AssociationTypeBatchMessageHandler.php
new file mode 100644
index 00000000..8316c5d8
--- /dev/null
+++ b/src/MessageHandler/Batch/AssociationTypeBatchMessageHandler.php
@@ -0,0 +1,35 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Synolia\SyliusAkeneoPlugin\MessageHandler\Batch;
+
+use Symfony\Component\Messenger\Attribute\AsMessageHandler;
+use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
+use Synolia\SyliusAkeneoPlugin\Message\Batch\AssociationTypeBatchMessage;
+use Synolia\SyliusAkeneoPlugin\Processor\Resource\AssociationType\AssociationTypeResourceProcessor;
+use Synolia\SyliusAkeneoPlugin\Processor\Resource\Exception\MaxResourceProcessorRetryException;
+
+#[AsMessageHandler]
+class AssociationTypeBatchMessageHandler
+{
+    public function __construct(
+        private EventDispatcherInterface $dispatcher,
+        private AssociationTypeResourceProcessor $resourceProcessor,
+    ) {
+    }
+
+    public function __invoke(AssociationTypeBatchMessage $attributeBatchMessage): void
+    {
+        foreach ($attributeBatchMessage->items as $resource) {
+            try {
+                $this->resourceProcessor->process($resource);
+            } catch (MaxResourceProcessorRetryException) {
+                // Skip the failing line
+                $this->dispatcher->dispatch(new AssociationTypeBatchMessage([$resource]));
+
+                continue;
+            }
+        }
+    }
+}
diff --git a/src/MessageHandler/Batch/AttributeBatchMessageHandler.php b/src/MessageHandler/Batch/AttributeBatchMessageHandler.php
new file mode 100644
index 00000000..3568e62b
--- /dev/null
+++ b/src/MessageHandler/Batch/AttributeBatchMessageHandler.php
@@ -0,0 +1,35 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Synolia\SyliusAkeneoPlugin\MessageHandler\Batch;
+
+use Symfony\Component\Messenger\Attribute\AsMessageHandler;
+use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
+use Synolia\SyliusAkeneoPlugin\Message\Batch\AttributeBatchMessage;
+use Synolia\SyliusAkeneoPlugin\Processor\Resource\Attribute\AttributeResourceProcessor;
+use Synolia\SyliusAkeneoPlugin\Processor\Resource\Exception\MaxResourceProcessorRetryException;
+
+#[AsMessageHandler]
+class AttributeBatchMessageHandler
+{
+    public function __construct(
+        private EventDispatcherInterface $dispatcher,
+        private AttributeResourceProcessor $resourceProcessor,
+    ) {
+    }
+
+    public function __invoke(AttributeBatchMessage $attributeBatchMessage): void
+    {
+        foreach ($attributeBatchMessage->items as $resource) {
+            try {
+                $this->resourceProcessor->process($resource);
+            } catch (MaxResourceProcessorRetryException) {
+                // Skip the failing line
+                $this->dispatcher->dispatch(new AttributeBatchMessage([$resource]));
+
+                continue;
+            }
+        }
+    }
+}
diff --git a/src/MessageHandler/Batch/ProductBatchMessageHandler.php b/src/MessageHandler/Batch/ProductBatchMessageHandler.php
new file mode 100644
index 00000000..a68733b3
--- /dev/null
+++ b/src/MessageHandler/Batch/ProductBatchMessageHandler.php
@@ -0,0 +1,35 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Synolia\SyliusAkeneoPlugin\MessageHandler\Batch;
+
+use Symfony\Component\Messenger\Attribute\AsMessageHandler;
+use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
+use Synolia\SyliusAkeneoPlugin\Message\Batch\ProductBatchMessage;
+use Synolia\SyliusAkeneoPlugin\Processor\Resource\Exception\MaxResourceProcessorRetryException;
+use Synolia\SyliusAkeneoPlugin\Processor\Resource\Product\ProductModelResourceProcessor;
+
+#[AsMessageHandler]
+class ProductBatchMessageHandler
+{
+    public function __construct(
+        private EventDispatcherInterface $dispatcher,
+        private ProductModelResourceProcessor $resourceProcessor,
+    ) {
+    }
+
+    public function __invoke(ProductBatchMessage $productModelBatchMessage): void
+    {
+        foreach ($productModelBatchMessage->items as $resource) {
+            try {
+                $this->resourceProcessor->process($resource);
+            } catch (MaxResourceProcessorRetryException) {
+                // Skip the failing line
+                $this->dispatcher->dispatch(new ProductBatchMessage([$resource]));
+
+                continue;
+            }
+        }
+    }
+}
diff --git a/src/MessageHandler/Batch/ProductVariantBatchMessageHandler.php b/src/MessageHandler/Batch/ProductVariantBatchMessageHandler.php
new file mode 100644
index 00000000..81fe0c07
--- /dev/null
+++ b/src/MessageHandler/Batch/ProductVariantBatchMessageHandler.php
@@ -0,0 +1,35 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Synolia\SyliusAkeneoPlugin\MessageHandler\Batch;
+
+use Symfony\Component\Messenger\Attribute\AsMessageHandler;
+use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
+use Synolia\SyliusAkeneoPlugin\Message\Batch\ProductVariantBatchMessage;
+use Synolia\SyliusAkeneoPlugin\Processor\Resource\Exception\MaxResourceProcessorRetryException;
+use Synolia\SyliusAkeneoPlugin\Processor\Resource\ProductVariant\ProductVariantResourceProcessor;
+
+#[AsMessageHandler]
+class ProductVariantBatchMessageHandler
+{
+    public function __construct(
+        private EventDispatcherInterface $dispatcher,
+        private ProductVariantResourceProcessor $resourceProcessor,
+    ) {
+    }
+
+    public function __invoke(ProductVariantBatchMessage $productVariantBatchMessage): void
+    {
+        foreach ($productVariantBatchMessage->items as $resource) {
+            try {
+                $this->resourceProcessor->process($resource);
+            } catch (MaxResourceProcessorRetryException) {
+                // Skip the failing line
+                $this->dispatcher->dispatch(new ProductVariantBatchMessage([$resource]));
+
+                continue;
+            }
+        }
+    }
+}
diff --git a/src/Payload/AbstractPayload.php b/src/Payload/AbstractPayload.php
index 6dca7945..2d94efc7 100644
--- a/src/Payload/AbstractPayload.php
+++ b/src/Payload/AbstractPayload.php
@@ -28,7 +28,7 @@ abstract class AbstractPayload implements PipelinePayloadInterface
 
     public function __construct(
         protected AkeneoPimClientInterface $akeneoPimClient,
-        protected ?\Synolia\SyliusAkeneoPlugin\Command\Context\CommandContextInterface $commandContext = null,
+        protected ?CommandContextInterface $commandContext = null,
     ) {
         if (null !== $commandContext) {
             $this->allowParallel = $commandContext->allowParallel();
diff --git a/src/Payload/Asset/AssetFamilyPayload.php b/src/Payload/Asset/AssetFamilyPayload.php
index b4b1cd56..1108643d 100644
--- a/src/Payload/Asset/AssetFamilyPayload.php
+++ b/src/Payload/Asset/AssetFamilyPayload.php
@@ -7,6 +7,7 @@
 use Akeneo\Pim\ApiClient\AkeneoPimClientInterface;
 use Akeneo\Pim\ApiClient\Pagination\ResourceCursorInterface;
 use Synolia\SyliusAkeneoPlugin\Command\Context\CommandContextInterface;
+use Synolia\SyliusAkeneoPlugin\Message\Batch\BatchMessageInterface;
 use Synolia\SyliusAkeneoPlugin\Payload\AbstractPayload;
 
 final class AssetFamilyPayload extends AbstractPayload
@@ -36,4 +37,9 @@ public function setResources(ResourceCursorInterface $resources): void
     {
         $this->resources = $resources;
     }
+
+    public function createBatchMessage(array $items): BatchMessageInterface
+    {
+        throw new \InvalidArgumentException();
+    }
 }
diff --git a/src/Payload/Asset/AssetPayload.php b/src/Payload/Asset/AssetPayload.php
index 67689bf8..9449262a 100644
--- a/src/Payload/Asset/AssetPayload.php
+++ b/src/Payload/Asset/AssetPayload.php
@@ -7,6 +7,7 @@
 use Akeneo\Pim\ApiClient\AkeneoPimClientInterface;
 use Akeneo\Pim\ApiClient\Pagination\ResourceCursorInterface;
 use Synolia\SyliusAkeneoPlugin\Command\Context\CommandContextInterface;
+use Synolia\SyliusAkeneoPlugin\Message\Batch\BatchMessageInterface;
 use Synolia\SyliusAkeneoPlugin\Payload\AbstractPayload;
 
 final class AssetPayload extends AbstractPayload
@@ -36,4 +37,9 @@ public function setResources(ResourceCursorInterface $resources): void
     {
         $this->resources = $resources;
     }
+
+    public function createBatchMessage(array $items): BatchMessageInterface
+    {
+        throw new \InvalidArgumentException();
+    }
 }
diff --git a/src/Payload/Association/AssociationPayload.php b/src/Payload/Association/AssociationPayload.php
index a18a41d3..eeb2a683 100644
--- a/src/Payload/Association/AssociationPayload.php
+++ b/src/Payload/Association/AssociationPayload.php
@@ -4,8 +4,13 @@
 
 namespace Synolia\SyliusAkeneoPlugin\Payload\Association;
 
+use Synolia\SyliusAkeneoPlugin\Message\Batch\BatchMessageInterface;
 use Synolia\SyliusAkeneoPlugin\Payload\AbstractPayload;
 
 final class AssociationPayload extends AbstractPayload
 {
+    public function createBatchMessage(array $items): BatchMessageInterface
+    {
+        throw new \InvalidArgumentException();
+    }
 }
diff --git a/src/Payload/Association/AssociationTypePayload.php b/src/Payload/Association/AssociationTypePayload.php
index 86dea0c0..bf6fdba5 100644
--- a/src/Payload/Association/AssociationTypePayload.php
+++ b/src/Payload/Association/AssociationTypePayload.php
@@ -7,6 +7,8 @@
 use Akeneo\Pim\ApiClient\AkeneoPimClientInterface;
 use Akeneo\Pim\ApiClient\Pagination\ResourceCursorInterface;
 use Synolia\SyliusAkeneoPlugin\Command\Context\CommandContextInterface;
+use Synolia\SyliusAkeneoPlugin\Message\Batch\AssociationTypeBatchMessage;
+use Synolia\SyliusAkeneoPlugin\Message\Batch\BatchMessageInterface;
 use Synolia\SyliusAkeneoPlugin\Payload\AbstractPayload;
 
 final class AssociationTypePayload extends AbstractPayload
@@ -36,4 +38,9 @@ public function setResources(ResourceCursorInterface $resources): void
     {
         $this->resources = $resources;
     }
+
+    public function createBatchMessage(array $items): BatchMessageInterface
+    {
+        return new AssociationTypeBatchMessage($items);
+    }
 }
diff --git a/src/Payload/Attribute/AttributePayload.php b/src/Payload/Attribute/AttributePayload.php
index eb35a16e..76ceba8c 100644
--- a/src/Payload/Attribute/AttributePayload.php
+++ b/src/Payload/Attribute/AttributePayload.php
@@ -7,6 +7,8 @@
 use Akeneo\Pim\ApiClient\AkeneoPimClientInterface;
 use Akeneo\Pim\ApiClient\Pagination\ResourceCursorInterface;
 use Synolia\SyliusAkeneoPlugin\Command\Context\CommandContextInterface;
+use Synolia\SyliusAkeneoPlugin\Message\Batch\AttributeBatchMessage;
+use Synolia\SyliusAkeneoPlugin\Message\Batch\BatchMessageInterface;
 use Synolia\SyliusAkeneoPlugin\Payload\AbstractPayload;
 
 final class AttributePayload extends AbstractPayload
@@ -36,4 +38,9 @@ public function setResources(ResourceCursorInterface $resources): void
     {
         $this->resources = $resources;
     }
+
+    public function createBatchMessage(array $items): BatchMessageInterface
+    {
+        return new AttributeBatchMessage($items);
+    }
 }
diff --git a/src/Payload/Attribute/LocaleAttributeTranslationPayload.php b/src/Payload/Attribute/LocaleAttributeTranslationPayload.php
index 967609bd..f6332ae8 100644
--- a/src/Payload/Attribute/LocaleAttributeTranslationPayload.php
+++ b/src/Payload/Attribute/LocaleAttributeTranslationPayload.php
@@ -6,6 +6,7 @@
 
 use Sylius\Component\Attribute\Model\AttributeInterface;
 use Sylius\Component\Core\Model\ProductInterface;
+use Synolia\SyliusAkeneoPlugin\Message\Batch\BatchMessageInterface;
 use Synolia\SyliusAkeneoPlugin\Payload\AbstractPayload;
 
 final class LocaleAttributeTranslationPayload extends AbstractPayload
@@ -93,4 +94,9 @@ public function setScope(string $scope): self
 
         return $this;
     }
+
+    public function createBatchMessage(array $items): BatchMessageInterface
+    {
+        throw new \InvalidArgumentException();
+    }
 }
diff --git a/src/Payload/Category/CategoryPayload.php b/src/Payload/Category/CategoryPayload.php
index 951376cf..6f650940 100644
--- a/src/Payload/Category/CategoryPayload.php
+++ b/src/Payload/Category/CategoryPayload.php
@@ -5,6 +5,7 @@
 namespace Synolia\SyliusAkeneoPlugin\Payload\Category;
 
 use Synolia\SyliusAkeneoPlugin\Exceptions\NoCategoryResourcesException;
+use Synolia\SyliusAkeneoPlugin\Message\Batch\BatchMessageInterface;
 use Synolia\SyliusAkeneoPlugin\Payload\AbstractPayload;
 
 final class CategoryPayload extends AbstractPayload
@@ -24,4 +25,9 @@ public function setResources(array $resources): void
     {
         $this->resources = $resources;
     }
+
+    public function createBatchMessage(array $items): BatchMessageInterface
+    {
+        throw new \InvalidArgumentException();
+    }
 }
diff --git a/src/Payload/ConfigurationPayload.php b/src/Payload/ConfigurationPayload.php
index 56712fbb..9b15bf2c 100644
--- a/src/Payload/ConfigurationPayload.php
+++ b/src/Payload/ConfigurationPayload.php
@@ -4,6 +4,13 @@
 
 namespace Synolia\SyliusAkeneoPlugin\Payload;
 
+use Synolia\SyliusAkeneoPlugin\Exceptions\NotImplementedException;
+use Synolia\SyliusAkeneoPlugin\Message\Batch\BatchMessageInterface;
+
 final class ConfigurationPayload extends AbstractPayload implements PipelinePayloadInterface
 {
+    public function createBatchMessage(array $items): BatchMessageInterface
+    {
+        throw new NotImplementedException();
+    }
 }
diff --git a/src/Payload/Option/OptionsPayload.php b/src/Payload/Option/OptionsPayload.php
deleted file mode 100644
index 3ee4b09c..00000000
--- a/src/Payload/Option/OptionsPayload.php
+++ /dev/null
@@ -1,40 +0,0 @@
-<?php
-
-declare(strict_types=1);
-
-namespace Synolia\SyliusAkeneoPlugin\Payload\Option;
-
-use Synolia\SyliusAkeneoPlugin\Payload\AbstractPayload;
-
-final class OptionsPayload extends AbstractPayload
-{
-    /** @var array<\Akeneo\Pim\ApiClient\Pagination\ResourceCursorInterface> */
-    private array $selectOptionsResources;
-
-    /** @var array<\Akeneo\Pim\ApiClient\Pagination\ResourceCursorInterface> */
-    private array $referenceEntityOptionsResources;
-
-    public function getSelectOptionsResources(): array
-    {
-        return $this->selectOptionsResources;
-    }
-
-    public function setSelectOptionsResources(array $selectOptionsResources): self
-    {
-        $this->selectOptionsResources = $selectOptionsResources;
-
-        return $this;
-    }
-
-    public function getReferenceEntityOptionsResources(): array
-    {
-        return $this->referenceEntityOptionsResources;
-    }
-
-    public function setReferenceEntityOptionsResources(array $referenceEntityOptionsResources): self
-    {
-        $this->referenceEntityOptionsResources = $referenceEntityOptionsResources;
-
-        return $this;
-    }
-}
diff --git a/src/Payload/PipelinePayloadInterface.php b/src/Payload/PipelinePayloadInterface.php
index 805ad44b..9a65513b 100644
--- a/src/Payload/PipelinePayloadInterface.php
+++ b/src/Payload/PipelinePayloadInterface.php
@@ -5,10 +5,13 @@
 namespace Synolia\SyliusAkeneoPlugin\Payload;
 
 use Akeneo\Pim\ApiClient\AkeneoPimClientInterface;
+use Synolia\SyliusAkeneoPlugin\Message\Batch\BatchMessageInterface;
 
 interface PipelinePayloadInterface extends PayloadInterface
 {
     public function getAkeneoPimClient(): AkeneoPimClientInterface;
 
     public function getType(): string;
+
+    public function createBatchMessage(array $items): BatchMessageInterface;
 }
diff --git a/src/Payload/Product/ProductCategoriesPayload.php b/src/Payload/Product/ProductCategoriesPayload.php
index 696827ba..835a3f41 100644
--- a/src/Payload/Product/ProductCategoriesPayload.php
+++ b/src/Payload/Product/ProductCategoriesPayload.php
@@ -5,6 +5,7 @@
 namespace Synolia\SyliusAkeneoPlugin\Payload\Product;
 
 use Sylius\Component\Core\Model\ProductInterface;
+use Synolia\SyliusAkeneoPlugin\Message\Batch\BatchMessageInterface;
 use Synolia\SyliusAkeneoPlugin\Payload\AbstractPayload;
 
 final class ProductCategoriesPayload extends AbstractPayload
@@ -36,4 +37,9 @@ public function setCategories(array $categories): self
 
         return $this;
     }
+
+    public function createBatchMessage(array $items): BatchMessageInterface
+    {
+        throw new \InvalidArgumentException();
+    }
 }
diff --git a/src/Payload/Product/ProductMediaPayload.php b/src/Payload/Product/ProductMediaPayload.php
index 3ffd606a..c601e975 100644
--- a/src/Payload/Product/ProductMediaPayload.php
+++ b/src/Payload/Product/ProductMediaPayload.php
@@ -6,6 +6,7 @@
 
 use Sylius\Component\Core\Model\ProductInterface;
 use Synolia\SyliusAkeneoPlugin\Entity\ProductConfiguration;
+use Synolia\SyliusAkeneoPlugin\Message\Batch\BatchMessageInterface;
 use Synolia\SyliusAkeneoPlugin\Payload\AbstractPayload;
 
 final class ProductMediaPayload extends AbstractPayload implements ProductMediaPayloadInterface
@@ -49,4 +50,9 @@ public function setProductConfiguration(ProductConfiguration $productConfigurati
     {
         $this->productConfiguration = $productConfiguration;
     }
+
+    public function createBatchMessage(array $items): BatchMessageInterface
+    {
+        throw new \InvalidArgumentException();
+    }
 }
diff --git a/src/Payload/Product/ProductPayload.php b/src/Payload/Product/ProductPayload.php
index 6f3df5c1..c63e68f1 100644
--- a/src/Payload/Product/ProductPayload.php
+++ b/src/Payload/Product/ProductPayload.php
@@ -7,6 +7,8 @@
 use Akeneo\Pim\ApiClient\AkeneoPimClientInterface;
 use Akeneo\Pim\ApiClient\Pagination\PageInterface;
 use Synolia\SyliusAkeneoPlugin\Command\Context\CommandContextInterface;
+use Synolia\SyliusAkeneoPlugin\Message\Batch\BatchMessageInterface;
+use Synolia\SyliusAkeneoPlugin\Message\Batch\ProductVariantBatchMessage;
 use Synolia\SyliusAkeneoPlugin\Payload\AbstractPayload;
 
 final class ProductPayload extends AbstractPayload
@@ -36,4 +38,9 @@ public function setResources(PageInterface $resources): void
     {
         $this->resources = $resources;
     }
+
+    public function createBatchMessage(array $items): BatchMessageInterface
+    {
+        return new ProductVariantBatchMessage($items);
+    }
 }
diff --git a/src/Payload/Product/ProductResourcePayload.php b/src/Payload/Product/ProductResourcePayload.php
index 6663cd64..2c645883 100644
--- a/src/Payload/Product/ProductResourcePayload.php
+++ b/src/Payload/Product/ProductResourcePayload.php
@@ -5,6 +5,7 @@
 namespace Synolia\SyliusAkeneoPlugin\Payload\Product;
 
 use Sylius\Component\Core\Model\ProductInterface;
+use Synolia\SyliusAkeneoPlugin\Message\Batch\BatchMessageInterface;
 use Synolia\SyliusAkeneoPlugin\Payload\AbstractPayload;
 
 final class ProductResourcePayload extends AbstractPayload
@@ -74,4 +75,9 @@ public function setScope(string $scope): self
 
         return $this;
     }
+
+    public function createBatchMessage(array $items): BatchMessageInterface
+    {
+        throw new \InvalidArgumentException();
+    }
 }
diff --git a/src/Payload/Product/ProductVariantMediaPayload.php b/src/Payload/Product/ProductVariantMediaPayload.php
index 2b8772d7..68ee90df 100644
--- a/src/Payload/Product/ProductVariantMediaPayload.php
+++ b/src/Payload/Product/ProductVariantMediaPayload.php
@@ -5,6 +5,7 @@
 namespace Synolia\SyliusAkeneoPlugin\Payload\Product;
 
 use Sylius\Component\Core\Model\ProductVariantInterface;
+use Synolia\SyliusAkeneoPlugin\Message\Batch\BatchMessageInterface;
 use Synolia\SyliusAkeneoPlugin\Payload\AbstractPayload;
 
 final class ProductVariantMediaPayload extends AbstractPayload implements ProductMediaPayloadInterface
@@ -36,4 +37,9 @@ public function setAttributes(array $attributes): self
 
         return $this;
     }
+
+    public function createBatchMessage(array $items): BatchMessageInterface
+    {
+        throw new \InvalidArgumentException();
+    }
 }
diff --git a/src/Payload/ProductModel/ProductModelPayload.php b/src/Payload/ProductModel/ProductModelPayload.php
index 6f6c2575..fd8ce7d1 100644
--- a/src/Payload/ProductModel/ProductModelPayload.php
+++ b/src/Payload/ProductModel/ProductModelPayload.php
@@ -7,6 +7,8 @@
 use Akeneo\Pim\ApiClient\AkeneoPimClientInterface;
 use Akeneo\Pim\ApiClient\Pagination\ResourceCursorInterface;
 use Synolia\SyliusAkeneoPlugin\Command\Context\CommandContextInterface;
+use Synolia\SyliusAkeneoPlugin\Message\Batch\BatchMessageInterface;
+use Synolia\SyliusAkeneoPlugin\Message\Batch\ProductBatchMessage;
 use Synolia\SyliusAkeneoPlugin\Payload\AbstractPayload;
 
 final class ProductModelPayload extends AbstractPayload
@@ -36,4 +38,9 @@ public function setResources(ResourceCursorInterface $resources): void
     {
         $this->resources = $resources;
     }
+
+    public function createBatchMessage(array $items): BatchMessageInterface
+    {
+        return new ProductBatchMessage($items);
+    }
 }
diff --git a/src/Payload/ReferenceEntity/ReferenceEntityOptionsPayload.php b/src/Payload/ReferenceEntity/ReferenceEntityOptionsPayload.php
index ecd9abc9..cbef215f 100644
--- a/src/Payload/ReferenceEntity/ReferenceEntityOptionsPayload.php
+++ b/src/Payload/ReferenceEntity/ReferenceEntityOptionsPayload.php
@@ -4,6 +4,7 @@
 
 namespace Synolia\SyliusAkeneoPlugin\Payload\ReferenceEntity;
 
+use Synolia\SyliusAkeneoPlugin\Message\Batch\BatchMessageInterface;
 use Synolia\SyliusAkeneoPlugin\Payload\AbstractPayload;
 
 final class ReferenceEntityOptionsPayload extends AbstractPayload
@@ -24,4 +25,9 @@ public function setResource(string $attributeCode, array $resources): void
     {
         $this->resources[$attributeCode] = $resources;
     }
+
+    public function createBatchMessage(array $items): BatchMessageInterface
+    {
+        throw new \InvalidArgumentException();
+    }
 }
diff --git a/src/Processor/Product/CompleteRequirementProcessor.php b/src/Processor/Product/CompleteRequirementProcessor.php
index d510d765..d5f573bb 100644
--- a/src/Processor/Product/CompleteRequirementProcessor.php
+++ b/src/Processor/Product/CompleteRequirementProcessor.php
@@ -16,8 +16,8 @@
 use Synolia\SyliusAkeneoPlugin\Exceptions\Attribute\MissingLocaleTranslationOrScopeException;
 use Synolia\SyliusAkeneoPlugin\Exceptions\Attribute\MissingScopeException;
 use Synolia\SyliusAkeneoPlugin\Exceptions\Attribute\TranslationNotFoundException;
-use Synolia\SyliusAkeneoPlugin\Provider\AkeneoAttributeDataProviderInterface;
 use Synolia\SyliusAkeneoPlugin\Provider\AkeneoFamilyPropertiesProviderInterface;
+use Synolia\SyliusAkeneoPlugin\Provider\Data\AkeneoAttributeDataProviderInterface;
 use Synolia\SyliusAkeneoPlugin\Provider\ProductFilterRulesProviderInterface;
 use Synolia\SyliusAkeneoPlugin\Provider\SyliusAkeneoLocaleCodeProvider;
 
diff --git a/src/Processor/ProductAttribute/AbstractModelAkeneoAttributeProcessor.php b/src/Processor/ProductAttribute/AbstractModelAkeneoAttributeProcessor.php
index e3f587af..8dbe4ad1 100644
--- a/src/Processor/ProductAttribute/AbstractModelAkeneoAttributeProcessor.php
+++ b/src/Processor/ProductAttribute/AbstractModelAkeneoAttributeProcessor.php
@@ -7,8 +7,8 @@
 use Psr\Log\LoggerInterface;
 use Sylius\Component\Resource\Model\ResourceInterface;
 use Symfony\Component\Serializer\NameConverter\CamelCaseToSnakeCaseNameConverter;
-use Synolia\SyliusAkeneoPlugin\Provider\AkeneoAttributeDataProviderInterface;
-use Synolia\SyliusAkeneoPlugin\Provider\AkeneoAttributePropertiesProvider;
+use Synolia\SyliusAkeneoPlugin\Provider\Data\AkeneoAttributeDataProviderInterface;
+use Synolia\SyliusAkeneoPlugin\Provider\Data\AkeneoAttributePropertiesProvider;
 use Synolia\SyliusAkeneoPlugin\Provider\SyliusAkeneoLocaleCodeProvider;
 
 abstract class AbstractModelAkeneoAttributeProcessor
diff --git a/src/Processor/ProductAttribute/AssetAttributeProcessor.php b/src/Processor/ProductAttribute/AssetAttributeProcessor.php
index d2949c28..c4aa1083 100644
--- a/src/Processor/ProductAttribute/AssetAttributeProcessor.php
+++ b/src/Processor/ProductAttribute/AssetAttributeProcessor.php
@@ -18,7 +18,7 @@
 use Synolia\SyliusAkeneoPlugin\Exceptions\Attribute\MissingScopeException;
 use Synolia\SyliusAkeneoPlugin\Exceptions\Attribute\TranslationNotFoundException;
 use Synolia\SyliusAkeneoPlugin\Exceptions\UnsupportedAttributeTypeException;
-use Synolia\SyliusAkeneoPlugin\Provider\AkeneoAttributeDataProviderInterface;
+use Synolia\SyliusAkeneoPlugin\Provider\Data\AkeneoAttributeDataProviderInterface;
 use Synolia\SyliusAkeneoPlugin\Provider\SyliusAkeneoLocaleCodeProvider;
 use Synolia\SyliusAkeneoPlugin\Transformer\AkeneoAttributeToSyliusAttributeTransformerInterface;
 use Webmozart\Assert\Assert;
diff --git a/src/Processor/ProductAttribute/ProductAttributeAkeneoAttributeProcessor.php b/src/Processor/ProductAttribute/ProductAttributeAkeneoAttributeProcessor.php
index 34a73920..e98316d1 100644
--- a/src/Processor/ProductAttribute/ProductAttributeAkeneoAttributeProcessor.php
+++ b/src/Processor/ProductAttribute/ProductAttributeAkeneoAttributeProcessor.php
@@ -16,7 +16,7 @@
 use Synolia\SyliusAkeneoPlugin\Exceptions\Attribute\MissingLocaleTranslationOrScopeException;
 use Synolia\SyliusAkeneoPlugin\Exceptions\Attribute\MissingScopeException;
 use Synolia\SyliusAkeneoPlugin\Exceptions\Attribute\TranslationNotFoundException;
-use Synolia\SyliusAkeneoPlugin\Provider\AkeneoAttributeDataProviderInterface;
+use Synolia\SyliusAkeneoPlugin\Provider\Data\AkeneoAttributeDataProviderInterface;
 use Synolia\SyliusAkeneoPlugin\Provider\SyliusAkeneoLocaleCodeProvider;
 use Synolia\SyliusAkeneoPlugin\Transformer\AkeneoAttributeToSyliusAttributeTransformerInterface;
 
diff --git a/src/Processor/ProductOptionValue/ReferenceEntityOptionValuesProcessor.php b/src/Processor/ProductOptionValue/ReferenceEntityOptionValuesProcessor.php
index ecbc5eb0..7e26bd2a 100644
--- a/src/Processor/ProductOptionValue/ReferenceEntityOptionValuesProcessor.php
+++ b/src/Processor/ProductOptionValue/ReferenceEntityOptionValuesProcessor.php
@@ -15,7 +15,7 @@
 use Sylius\Component\Resource\Repository\RepositoryInterface;
 use Synolia\SyliusAkeneoPlugin\Checker\EditionCheckerInterface;
 use Synolia\SyliusAkeneoPlugin\Component\Attribute\AttributeType\ReferenceEntityAttributeType;
-use Synolia\SyliusAkeneoPlugin\Provider\AkeneoAttributePropertiesProvider;
+use Synolia\SyliusAkeneoPlugin\Provider\Data\AkeneoAttributePropertiesProvider;
 use Synolia\SyliusAkeneoPlugin\Repository\LocaleRepositoryInterface;
 use Synolia\SyliusAkeneoPlugin\Transformer\ProductOptionValueDataTransformerInterface;
 use Webmozart\Assert\Assert;
diff --git a/src/Processor/Resource/AkeneoResourceProcessorInterface.php b/src/Processor/Resource/AkeneoResourceProcessorInterface.php
new file mode 100644
index 00000000..0e80bec3
--- /dev/null
+++ b/src/Processor/Resource/AkeneoResourceProcessorInterface.php
@@ -0,0 +1,10 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Synolia\SyliusAkeneoPlugin\Processor\Resource;
+
+interface AkeneoResourceProcessorInterface
+{
+    public function process(array $resource): void;
+}
diff --git a/src/Processor/Resource/AssociationType/AssociationTypeResourceProcessor.php b/src/Processor/Resource/AssociationType/AssociationTypeResourceProcessor.php
new file mode 100644
index 00000000..91fb3f23
--- /dev/null
+++ b/src/Processor/Resource/AssociationType/AssociationTypeResourceProcessor.php
@@ -0,0 +1,113 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Synolia\SyliusAkeneoPlugin\Processor\Resource\AssociationType;
+
+use Doctrine\ORM\EntityManagerInterface;
+use Doctrine\ORM\ORMInvalidArgumentException;
+use Doctrine\Persistence\ManagerRegistry;
+use Psr\Log\LoggerInterface;
+use Sylius\Component\Product\Model\ProductAssociationTypeInterface;
+use Sylius\Component\Product\Repository\ProductAssociationTypeRepositoryInterface;
+use Sylius\Component\Resource\Factory\FactoryInterface;
+use Synolia\SyliusAkeneoPlugin\Processor\Resource\AkeneoResourceProcessorInterface;
+use Synolia\SyliusAkeneoPlugin\Processor\Resource\Exception\MaxResourceProcessorRetryException;
+use Synolia\SyliusAkeneoPlugin\Provider\SyliusAkeneoLocaleCodeProvider;
+
+class AssociationTypeResourceProcessor implements AkeneoResourceProcessorInterface
+{
+    public function __construct(
+        private EntityManagerInterface $entityManager,
+        private LoggerInterface $akeneoLogger,
+        private FactoryInterface $productAssociationTypeFactory,
+        private ProductAssociationTypeRepositoryInterface $productAssociationTypeRepository,
+        private SyliusAkeneoLocaleCodeProvider $syliusAkeneoLocaleCodeProvider,
+        private ManagerRegistry $managerRegistry,
+        private int $maxRetryCount,
+        private int $retryWaitTime,
+        private int $retryCount = 0,
+    ) {
+    }
+
+    /**
+     * @throws MaxResourceProcessorRetryException
+     */
+    public function process(array $resource): void
+    {
+        if ($this->retryCount === $this->maxRetryCount) {
+            throw new MaxResourceProcessorRetryException();
+        }
+
+        try {
+            $this->akeneoLogger->notice('Association Type', [
+                'code' => $resource['code'] ?? 'unknown',
+            ]);
+
+            $productAssociationType = $this->productAssociationTypeRepository->findOneBy(['code' => $resource['code']]);
+            if (!$productAssociationType instanceof ProductAssociationTypeInterface) {
+                /** @var ProductAssociationTypeInterface $productAssociationType */
+                $productAssociationType = $this->productAssociationTypeFactory->createNew();
+                $this->entityManager->persist($productAssociationType);
+
+                $productAssociationType->setCode($resource['code']);
+            }
+
+            $this->setTranslations($resource['labels'], $productAssociationType);
+
+            $this->entityManager->flush();
+        } catch (ORMInvalidArgumentException $ormInvalidArgumentException) {
+            ++$this->retryCount;
+            usleep($this->retryWaitTime);
+
+            $this->akeneoLogger->error('Retrying import', [
+                'product' => $resource,
+                'retry_count' => $this->retryCount,
+                'error' => $ormInvalidArgumentException->getMessage(),
+            ]);
+
+            $this->entityManager = $this->getNewEntityManager();
+            $this->process($resource);
+        } catch (\Throwable $throwable) {
+            ++$this->retryCount;
+            usleep($this->retryWaitTime);
+
+            $this->akeneoLogger->error('Retrying import', [
+                'message' => $throwable->getMessage(),
+                'trace' => $throwable->getTraceAsString(),
+            ]);
+
+            $this->entityManager = $this->getNewEntityManager();
+            $this->process($resource);
+        }
+    }
+
+    private function setTranslations(array $labels, ProductAssociationTypeInterface $productAssociationType): void
+    {
+        foreach ($this->syliusAkeneoLocaleCodeProvider->getUsedLocalesOnBothPlatforms() as $usedLocalesOnBothPlatform) {
+            $akeneoLocale = $this->syliusAkeneoLocaleCodeProvider->getAkeneoLocale($usedLocalesOnBothPlatform);
+
+            $productAssociationType->setCurrentLocale($usedLocalesOnBothPlatform);
+            $productAssociationType->setFallbackLocale($usedLocalesOnBothPlatform);
+
+            if (!isset($labels[$akeneoLocale])) {
+                $productAssociationType->setName(sprintf('[%s]', $productAssociationType->getCode()));
+
+                continue;
+            }
+
+            $productAssociationType->setName($labels[$akeneoLocale]);
+        }
+    }
+
+    private function getNewEntityManager(): EntityManagerInterface
+    {
+        $objectManager = $this->managerRegistry->resetManager();
+
+        if (!$objectManager instanceof EntityManagerInterface) {
+            throw new \LogicException('Wrong ObjectManager');
+        }
+
+        return $objectManager;
+    }
+}
diff --git a/src/Processor/Resource/Attribute/AttributeResourceProcessor.php b/src/Processor/Resource/Attribute/AttributeResourceProcessor.php
new file mode 100644
index 00000000..29bb6fa8
--- /dev/null
+++ b/src/Processor/Resource/Attribute/AttributeResourceProcessor.php
@@ -0,0 +1,149 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Synolia\SyliusAkeneoPlugin\Processor\Resource\Attribute;
+
+use Doctrine\ORM\EntityManagerInterface;
+use Doctrine\ORM\ORMInvalidArgumentException;
+use Doctrine\Persistence\ManagerRegistry;
+use Psr\Log\LoggerInterface;
+use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
+use Synolia\SyliusAkeneoPlugin\Creator\AttributeCreatorInterface;
+use Synolia\SyliusAkeneoPlugin\Event\Attribute\AfterProcessingAttributeEvent;
+use Synolia\SyliusAkeneoPlugin\Event\Attribute\BeforeProcessingAttributeEvent;
+use Synolia\SyliusAkeneoPlugin\Exceptions\Attribute\ExcludedAttributeException;
+use Synolia\SyliusAkeneoPlugin\Processor\ProductAttribute\ProductAttributeChoiceProcessorInterface;
+use Synolia\SyliusAkeneoPlugin\Processor\ProductAttribute\ProductAttributeTableProcessorInterface;
+use Synolia\SyliusAkeneoPlugin\Processor\ProductOption\ProductOptionProcessorInterface;
+use Synolia\SyliusAkeneoPlugin\Processor\Resource\AkeneoResourceProcessorInterface;
+use Synolia\SyliusAkeneoPlugin\Processor\Resource\Exception\MaxResourceProcessorRetryException;
+use Synolia\SyliusAkeneoPlugin\Retriever\FamilyRetrieverInterface;
+use Synolia\SyliusAkeneoPlugin\Retriever\FamilyVariantRetrieverInterface;
+
+class AttributeResourceProcessor implements AkeneoResourceProcessorInterface
+{
+    public function __construct(
+        private EntityManagerInterface $entityManager,
+        private ProductAttributeChoiceProcessorInterface $attributeChoiceProcessor,
+        private ProductOptionProcessorInterface $productOptionProcessor,
+        private ProductAttributeTableProcessorInterface $productAttributeTableProcessor,
+        private EventDispatcherInterface $dispatcher,
+        private AttributeCreatorInterface $attributeCreator,
+        private FamilyRetrieverInterface $familyRetriever,
+        private FamilyVariantRetrieverInterface $familyVariantRetriever,
+        private LoggerInterface $akeneoLogger,
+        private ManagerRegistry $managerRegistry,
+        private int $maxRetryCount,
+        private int $retryWaitTime,
+        private int $retryCount = 0,
+    ) {
+    }
+
+    /**
+     * @throws MaxResourceProcessorRetryException
+     */
+    public function process(array $resource): void
+    {
+        if ($this->retryCount === $this->maxRetryCount) {
+            throw new MaxResourceProcessorRetryException();
+        }
+
+        $variationAxes = array_unique($this->getVariationAxes());
+
+        try {
+            $this->akeneoLogger->notice('Processing attribute', [
+                'code' => $resource['code'] ?? 'unknown',
+            ]);
+
+            $this->dispatcher->dispatch(new BeforeProcessingAttributeEvent($resource));
+
+            if (!$this->entityManager->getConnection()->isTransactionActive()) {
+                $this->entityManager->beginTransaction();
+            }
+
+            $attribute = $this->attributeCreator->create($resource);
+            $this->entityManager->flush();
+
+            //Handle attribute options
+            $this->attributeChoiceProcessor->process($attribute, $resource);
+
+            //Handle attribute table configuration
+            $this->productAttributeTableProcessor->process($attribute, $resource);
+
+            //Handler options
+            $this->productOptionProcessor->process($attribute, $variationAxes);
+
+            $this->dispatcher->dispatch(new AfterProcessingAttributeEvent($resource, $attribute));
+
+            $this->entityManager->flush();
+        } catch (ExcludedAttributeException) {
+            // Do nothing
+        } catch (ORMInvalidArgumentException $ormInvalidArgumentException) {
+            ++$this->retryCount;
+            usleep($this->retryWaitTime);
+
+            $this->akeneoLogger->error('Retrying import', [
+                'product' => $resource,
+                'retry_count' => $this->retryCount,
+                'error' => $ormInvalidArgumentException->getMessage(),
+            ]);
+
+            $this->entityManager = $this->getNewEntityManager();
+            $this->process($resource);
+        } catch (\Throwable $throwable) {
+            ++$this->retryCount;
+            usleep($this->retryWaitTime);
+
+            $this->akeneoLogger->error('Retrying import', [
+                'message' => $throwable->getMessage(),
+                'trace' => $throwable->getTraceAsString(),
+            ]);
+
+            $this->entityManager = $this->getNewEntityManager();
+            $this->process($resource);
+        }
+    }
+
+    private function getVariationAxes(): array
+    {
+        $variationAxes = [];
+        $families = $this->familyRetriever->getFamilies();
+
+        foreach ($families as $family) {
+            $familyVariants = $this->familyVariantRetriever->getVariants($family['code']);
+
+            $variationAxes = array_merge($variationAxes, $this->getVariationAxesForFamilies($familyVariants));
+        }
+
+        return $variationAxes;
+    }
+
+    private function getVariationAxesForFamilies(array $familyVariants): array
+    {
+        $variationAxes = [];
+
+        /** @var array{variant_attribute_sets: array} $familyVariant */
+        foreach ($familyVariants as $familyVariant) {
+            /** @var array{axes: array} $variantAttributeSet */
+            foreach ($familyVariant['variant_attribute_sets'] as $variantAttributeSet) {
+                foreach ($variantAttributeSet['axes'] as $axe) {
+                    $variationAxes[] = $axe;
+                }
+            }
+        }
+
+        return $variationAxes;
+    }
+
+    private function getNewEntityManager(): EntityManagerInterface
+    {
+        $objectManager = $this->managerRegistry->resetManager();
+
+        if (!$objectManager instanceof EntityManagerInterface) {
+            throw new \LogicException('Wrong ObjectManager');
+        }
+
+        return $objectManager;
+    }
+}
diff --git a/src/Processor/Resource/Exception/MaxResourceProcessorRetryException.php b/src/Processor/Resource/Exception/MaxResourceProcessorRetryException.php
new file mode 100644
index 00000000..1db096b2
--- /dev/null
+++ b/src/Processor/Resource/Exception/MaxResourceProcessorRetryException.php
@@ -0,0 +1,9 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Synolia\SyliusAkeneoPlugin\Processor\Resource\Exception;
+
+class MaxResourceProcessorRetryException extends \Exception
+{
+}
diff --git a/src/Processor/Resource/Exception/ResourceProcessorFailedException.php b/src/Processor/Resource/Exception/ResourceProcessorFailedException.php
new file mode 100644
index 00000000..ca03e635
--- /dev/null
+++ b/src/Processor/Resource/Exception/ResourceProcessorFailedException.php
@@ -0,0 +1,9 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Synolia\SyliusAkeneoPlugin\Processor\Resource\Exception;
+
+class ResourceProcessorFailedException extends \Exception
+{
+}
diff --git a/src/Processor/Resource/Product/ProductModelResourceProcessor.php b/src/Processor/Resource/Product/ProductModelResourceProcessor.php
new file mode 100644
index 00000000..b77a1830
--- /dev/null
+++ b/src/Processor/Resource/Product/ProductModelResourceProcessor.php
@@ -0,0 +1,142 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Synolia\SyliusAkeneoPlugin\Processor\Resource\Product;
+
+use Doctrine\ORM\EntityManagerInterface;
+use Doctrine\ORM\ORMInvalidArgumentException;
+use Doctrine\Persistence\ManagerRegistry;
+use Psr\Log\LoggerInterface;
+use Sylius\Component\Core\Model\ProductInterface;
+use Sylius\Component\Core\Repository\ProductRepositoryInterface;
+use Sylius\Component\Product\Factory\ProductFactoryInterface;
+use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
+use Synolia\SyliusAkeneoPlugin\Checker\Product\IsProductProcessableCheckerInterface;
+use Synolia\SyliusAkeneoPlugin\Event\Product\AfterProcessingProductEvent;
+use Synolia\SyliusAkeneoPlugin\Event\Product\BeforeProcessingProductEvent;
+use Synolia\SyliusAkeneoPlugin\Logger\Messages;
+use Synolia\SyliusAkeneoPlugin\Processor\Product\ProductProcessorChainInterface;
+use Synolia\SyliusAkeneoPlugin\Processor\ProductGroup\ProductGroupProcessor;
+use Synolia\SyliusAkeneoPlugin\Processor\Resource\AkeneoResourceProcessorInterface;
+use Synolia\SyliusAkeneoPlugin\Processor\Resource\Exception\MaxResourceProcessorRetryException;
+
+class ProductModelResourceProcessor implements AkeneoResourceProcessorInterface
+{
+    public function __construct(
+        private EntityManagerInterface $entityManager,
+        private ProductFactoryInterface $productFactory,
+        private ProductRepositoryInterface $productRepository,
+        private LoggerInterface $akeneoLogger,
+        private EventDispatcherInterface $dispatcher,
+        private ProductProcessorChainInterface $productProcessorChain,
+        private IsProductProcessableCheckerInterface $isProductProcessableChecker,
+        private ProductGroupProcessor $productGroupProcessor,
+        private ManagerRegistry $managerRegistry,
+        private int $maxRetryCount,
+        private int $retryWaitTime,
+        private int $retryCount = 0,
+    ) {
+    }
+
+    /**
+     * @throws MaxResourceProcessorRetryException
+     */
+    public function process(array $resource): void
+    {
+        if ($this->retryCount === $this->maxRetryCount) {
+            throw new MaxResourceProcessorRetryException();
+        }
+
+        try {
+            $this->akeneoLogger->notice('Processing product', [
+                'code' => $resource['code'] ?? $resource['identifier'] ?? 'unknown',
+            ]);
+
+            $this->handleProductGroup($resource);
+            $this->dispatcher->dispatch(new BeforeProcessingProductEvent($resource));
+
+            if (!$this->isProductProcessableChecker->check($resource)) {
+                return;
+            }
+
+            $product = $this->getOrCreateEntity($resource);
+            $this->productProcessorChain->chain($product, $resource);
+
+            // TODO: check if id is null
+            $this->akeneoLogger->info(Messages::hasBeenCreated($product::class, (string) $product->getCode()));
+            $this->akeneoLogger->info(Messages::hasBeenUpdated($product::class, (string) $resource['code']));
+
+            $this->dispatcher->dispatch(new AfterProcessingProductEvent($resource, $product));
+            $this->entityManager->flush();
+        } catch (ORMInvalidArgumentException $ormInvalidArgumentException) {
+            ++$this->retryCount;
+            usleep($this->retryWaitTime);
+
+            $this->akeneoLogger->error('Retrying import', [
+                'product' => $resource,
+                'retry_count' => $this->retryCount,
+                'error' => $ormInvalidArgumentException->getMessage(),
+            ]);
+
+            $this->entityManager = $this->getNewEntityManager();
+            $this->process($resource);
+        } catch (\Throwable $throwable) {
+            ++$this->retryCount;
+            usleep($this->retryWaitTime);
+
+            $this->akeneoLogger->error('Retrying import', [
+                'message' => $throwable->getMessage(),
+                'trace' => $throwable->getTraceAsString(),
+            ]);
+
+            $this->entityManager = $this->getNewEntityManager();
+            $this->process($resource);
+        }
+    }
+
+    private function handleProductGroup(array $resource): void
+    {
+        try {
+            $this->productGroupProcessor->process($resource);
+            $this->entityManager->flush();
+        } catch (ORMInvalidArgumentException $ormInvalidArgumentException) {
+            if (!$this->entityManager->isOpen()) {
+                $this->akeneoLogger->warning('Recreating entity manager', ['exception' => $ormInvalidArgumentException]);
+                $this->entityManager = $this->getNewEntityManager();
+            }
+
+            ++$this->retryCount;
+
+            throw $ormInvalidArgumentException;
+        }
+    }
+
+    private function getOrCreateEntity(array &$resource): ProductInterface
+    {
+        $product = $this->productRepository->findOneByCode($resource['code']);
+
+        if (!$product instanceof ProductInterface) {
+            /** @var ProductInterface $product */
+            $product = $this->productFactory->createNew();
+            $product->setCode($resource['code']);
+
+            $this->entityManager->persist($product);
+
+            return $product;
+        }
+
+        return $product;
+    }
+
+    private function getNewEntityManager(): EntityManagerInterface
+    {
+        $objectManager = $this->managerRegistry->resetManager();
+
+        if (!$objectManager instanceof EntityManagerInterface) {
+            throw new \LogicException('Wrong ObjectManager');
+        }
+
+        return $objectManager;
+    }
+}
diff --git a/src/Task/Product/ConfigurableProductsTask.php b/src/Processor/Resource/ProductVariant/ConfigurableProductVariantResourceProcessor.php
similarity index 65%
rename from src/Task/Product/ConfigurableProductsTask.php
rename to src/Processor/Resource/ProductVariant/ConfigurableProductVariantResourceProcessor.php
index e58dfc4a..b07d2f1b 100644
--- a/src/Task/Product/ConfigurableProductsTask.php
+++ b/src/Processor/Resource/ProductVariant/ConfigurableProductVariantResourceProcessor.php
@@ -2,7 +2,7 @@
 
 declare(strict_types=1);
 
-namespace Synolia\SyliusAkeneoPlugin\Task\Product;
+namespace Synolia\SyliusAkeneoPlugin\Processor\Resource\ProductVariant;
 
 use Doctrine\ORM\EntityManagerInterface;
 use Psr\Log\LoggerInterface;
@@ -11,68 +11,73 @@
 use Sylius\Component\Product\Factory\ProductVariantFactoryInterface;
 use Sylius\Component\Resource\Repository\RepositoryInterface;
 use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
-use Synolia\SyliusAkeneoPlugin\Client\ClientFactoryInterface;
 use Synolia\SyliusAkeneoPlugin\Config\AkeneoAxesEnum;
 use Synolia\SyliusAkeneoPlugin\Entity\ProductGroup;
 use Synolia\SyliusAkeneoPlugin\Entity\ProductGroupInterface;
 use Synolia\SyliusAkeneoPlugin\Event\ProductVariant\AfterProcessingProductVariantEvent;
 use Synolia\SyliusAkeneoPlugin\Event\ProductVariant\BeforeProcessingProductVariantEvent;
-use Synolia\SyliusAkeneoPlugin\Payload\PipelinePayloadInterface;
-use Synolia\SyliusAkeneoPlugin\Payload\Product\ProductPayload;
-use Synolia\SyliusAkeneoPlugin\Processor\Product\ProductChannelEnablerProcessorInterface;
 use Synolia\SyliusAkeneoPlugin\Processor\ProductVariant\ProductVariantProcessorChainInterface;
+use Synolia\SyliusAkeneoPlugin\Processor\Resource\AkeneoResourceProcessorInterface;
 use Synolia\SyliusAkeneoPlugin\Provider\Configuration\Api\ApiConnectionProviderInterface;
-use Synolia\SyliusAkeneoPlugin\Repository\ChannelRepository;
-use Synolia\SyliusAkeneoPlugin\Repository\LocaleRepositoryInterface;
 use Synolia\SyliusAkeneoPlugin\Repository\ProductGroupRepository;
+use Synolia\SyliusAkeneoPlugin\Retriever\FamilyVariantRetrieverInterface;
 use Throwable;
 
-final class ConfigurableProductsTask extends AbstractCreateProductEntities
+class ConfigurableProductVariantResourceProcessor implements AkeneoResourceProcessorInterface, ProductVariantAkeneoResourceProcessorInterface
 {
-    /**
-     * @SuppressWarnings(PHPMD.ExcessiveParameterList)
-     */
     public function __construct(
-        EntityManagerInterface $entityManager,
-        RepositoryInterface $productVariantRepository,
-        RepositoryInterface $productRepository,
-        ChannelRepository $channelRepository,
-        LocaleRepositoryInterface $localeRepository,
-        RepositoryInterface $productConfigurationRepository,
+        private EntityManagerInterface $entityManager,
+        private RepositoryInterface $productVariantRepository,
+        private RepositoryInterface $productRepository,
         private ProductGroupRepository $productGroupRepository,
-        ProductVariantFactoryInterface $productVariantFactory,
-        LoggerInterface $akeneoLogger,
+        private ProductVariantFactoryInterface $productVariantFactory,
+        private LoggerInterface $akeneoLogger,
         private EventDispatcherInterface $dispatcher,
-        ProductChannelEnablerProcessorInterface $productChannelEnabler,
         private ProductVariantProcessorChainInterface $productVariantProcessorChain,
-        private ClientFactoryInterface $clientFactory,
         private ApiConnectionProviderInterface $apiConnectionProvider,
+        private FamilyVariantRetrieverInterface $familyVariantRetriever,
     ) {
-        parent::__construct(
-            $entityManager,
-            $productVariantRepository,
-            $productRepository,
-            $channelRepository,
-            $localeRepository,
-            $productConfigurationRepository,
-            $productVariantFactory,
-            $akeneoLogger,
-            $productChannelEnabler,
-        );
     }
 
-    /**
-     * @param ProductPayload $payload
-     *
-     * @inheritDoc
-     */
-    public function __invoke(PipelinePayloadInterface $payload, array $resource): void
+    private function getModelCode(ProductGroupInterface $productGroup): string
+    {
+        if ($this->apiConnectionProvider->get()->getAxeAsModel() === AkeneoAxesEnum::COMMON &&
+            $productGroup->getParent() !== null) {
+            return $productGroup->getParent()->getModel();
+        }
+
+        return $productGroup->getModel();
+    }
+
+    private function getOrCreateEntity(string $variantCode, ProductInterface $productModel): ProductVariantInterface
+    {
+        $productVariant = $this->productVariantRepository->findOneBy(['code' => $variantCode]);
+
+        if (!$productVariant instanceof ProductVariantInterface) {
+            /** @var ProductVariantInterface $productVariant */
+            $productVariant = $this->productVariantFactory->createForProduct($productModel);
+            $productVariant->setCode($variantCode);
+
+            $this->entityManager->persist($productVariant);
+
+            return $productVariant;
+        }
+
+        return $productVariant;
+    }
+
+    public function support(array $resource): bool
+    {
+        return null !== $resource['parent'];
+    }
+
+    public function process(array $resource): void
     {
         try {
             $productGroup = $this->productGroupRepository->findOneBy(['model' => $resource['parent']]);
 
             if (!$productGroup instanceof ProductGroup) {
-                $this->logger->warning(sprintf(
+                $this->akeneoLogger->warning(sprintf(
                     'Skipped product "%s" because model "%s" does not exist as group.',
                     $resource['identifier'],
                     $resource['parent'],
@@ -81,7 +86,7 @@ public function __invoke(PipelinePayloadInterface $payload, array $resource): vo
                 return;
             }
 
-            $this->logger->info(sprintf(
+            $this->akeneoLogger->info(sprintf(
                 'Processing product "%s" on model "%s".',
                 $resource['identifier'],
                 $resource['parent'],
@@ -92,7 +97,7 @@ public function __invoke(PipelinePayloadInterface $payload, array $resource): vo
 
             //Skip product variant import if it does not have a parent model on Sylius
             if (!$productModel instanceof ProductInterface || !\is_string($productModel->getCode())) {
-                $this->logger->warning(sprintf(
+                $this->akeneoLogger->warning(sprintf(
                     'Skipped product "%s" because model "%s" does not exist.',
                     $resource['identifier'],
                     $resource['parent'],
@@ -103,17 +108,10 @@ public function __invoke(PipelinePayloadInterface $payload, array $resource): vo
 
             $this->dispatcher->dispatch(new BeforeProcessingProductVariantEvent($resource, $productModel));
 
-            $familyVariantPayload = $this->clientFactory
-                ->createFromApiCredentials()
-                ->getFamilyVariantApi()
-                ->get(
-                    $resource['family'],
-                    $productGroup->getFamilyVariant(),
-                )
-            ;
+            $familyVariantPayload = $this->familyVariantRetriever->getVariant($resource['family'], $productGroup->getFamilyVariant());
 
             if (0 === (is_countable($familyVariantPayload['variant_attribute_sets']) ? \count($familyVariantPayload['variant_attribute_sets']) : 0)) {
-                $this->logger->warning(sprintf(
+                $this->akeneoLogger->warning(sprintf(
                     'Skipped product "%s" because group has no variation axis.',
                     $resource['identifier'],
                 ));
@@ -125,38 +123,10 @@ public function __invoke(PipelinePayloadInterface $payload, array $resource): vo
             $this->productVariantProcessorChain->chain($productVariant, $resource);
 
             $this->dispatcher->dispatch(new AfterProcessingProductVariantEvent($resource, $productVariant));
-            $this->entityManager->flush();
         } catch (Throwable $throwable) {
-            $this->logger->warning($throwable->getMessage(), [
+            $this->akeneoLogger->warning($throwable->getMessage(), [
                 'exception' => $throwable,
             ]);
         }
     }
-
-    private function getModelCode(ProductGroupInterface $productGroup): string
-    {
-        if ($this->apiConnectionProvider->get()->getAxeAsModel() === AkeneoAxesEnum::COMMON &&
-            $productGroup->getParent() !== null) {
-            return $productGroup->getParent()->getModel();
-        }
-
-        return $productGroup->getModel();
-    }
-
-    private function getOrCreateEntity(string $variantCode, ProductInterface $productModel): ProductVariantInterface
-    {
-        $productVariant = $this->productVariantRepository->findOneBy(['code' => $variantCode]);
-
-        if (!$productVariant instanceof ProductVariantInterface) {
-            /** @var ProductVariantInterface $productVariant */
-            $productVariant = $this->productVariantFactory->createForProduct($productModel);
-            $productVariant->setCode($variantCode);
-
-            $this->entityManager->persist($productVariant);
-
-            return $productVariant;
-        }
-
-        return $productVariant;
-    }
 }
diff --git a/src/Processor/Resource/ProductVariant/ProductVariantAkeneoResourceProcessorInterface.php b/src/Processor/Resource/ProductVariant/ProductVariantAkeneoResourceProcessorInterface.php
new file mode 100644
index 00000000..d412cfc7
--- /dev/null
+++ b/src/Processor/Resource/ProductVariant/ProductVariantAkeneoResourceProcessorInterface.php
@@ -0,0 +1,15 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Synolia\SyliusAkeneoPlugin\Processor\Resource\ProductVariant;
+
+use Symfony\Component\DependencyInjection\Attribute\AutoconfigureTag;
+
+#[AutoconfigureTag()]
+interface ProductVariantAkeneoResourceProcessorInterface
+{
+    public function support(array $resource): bool;
+
+    public function process(array $resource): void;
+}
diff --git a/src/Processor/Resource/ProductVariant/ProductVariantResourceProcessor.php b/src/Processor/Resource/ProductVariant/ProductVariantResourceProcessor.php
new file mode 100644
index 00000000..4010d94c
--- /dev/null
+++ b/src/Processor/Resource/ProductVariant/ProductVariantResourceProcessor.php
@@ -0,0 +1,89 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Synolia\SyliusAkeneoPlugin\Processor\Resource\ProductVariant;
+
+use Doctrine\ORM\EntityManagerInterface;
+use Doctrine\ORM\ORMInvalidArgumentException;
+use Doctrine\Persistence\ManagerRegistry;
+use Psr\Log\LoggerInterface;
+use Symfony\Component\DependencyInjection\Attribute\TaggedIterator;
+use Synolia\SyliusAkeneoPlugin\Processor\Resource\AkeneoResourceProcessorInterface;
+use Synolia\SyliusAkeneoPlugin\Processor\Resource\Exception\MaxResourceProcessorRetryException;
+
+class ProductVariantResourceProcessor implements AkeneoResourceProcessorInterface
+{
+    /**
+     * @param ProductVariantAkeneoResourceProcessorInterface[] $productVariantAkeneoResourceProcessors
+     */
+    public function __construct(
+        private EntityManagerInterface $entityManager,
+        private LoggerInterface $akeneoLogger,
+        private ManagerRegistry $managerRegistry,
+        private int $maxRetryCount,
+        private int $retryWaitTime,
+        private int $retryCount = 0,
+        #[TaggedIterator(ProductVariantAkeneoResourceProcessorInterface::class)]
+        private iterable $productVariantAkeneoResourceProcessors = [],
+    ) {
+    }
+
+    /**
+     * @throws MaxResourceProcessorRetryException
+     */
+    public function process(array $resource): void
+    {
+        if ($this->retryCount === $this->maxRetryCount) {
+            throw new MaxResourceProcessorRetryException();
+        }
+
+        try {
+            $this->akeneoLogger->notice('Processing product variant', [
+                'code' => $resource['code'] ?? $resource['identifier'] ?? 'unknown',
+            ]);
+
+            foreach ($this->productVariantAkeneoResourceProcessors as $productVariantAkeneoResourceProcessor) {
+                if ($productVariantAkeneoResourceProcessor->support($resource)) {
+                    $productVariantAkeneoResourceProcessor->process($resource);
+                }
+            }
+
+            $this->entityManager->flush();
+        } catch (ORMInvalidArgumentException $ormInvalidArgumentException) {
+            ++$this->retryCount;
+            usleep($this->retryWaitTime);
+
+            $this->akeneoLogger->error('Retrying import', [
+                'product' => $resource,
+                'retry_count' => $this->retryCount,
+                'error' => $ormInvalidArgumentException->getMessage(),
+            ]);
+
+            $this->entityManager = $this->getNewEntityManager();
+            $this->process($resource);
+        } catch (\Throwable $throwable) {
+            ++$this->retryCount;
+            usleep($this->retryWaitTime);
+
+            $this->akeneoLogger->error('Retrying import', [
+                'message' => $throwable->getMessage(),
+                'trace' => $throwable->getTraceAsString(),
+            ]);
+
+            $this->entityManager = $this->getNewEntityManager();
+            $this->process($resource);
+        }
+    }
+
+    private function getNewEntityManager(): EntityManagerInterface
+    {
+        $objectManager = $this->managerRegistry->resetManager();
+
+        if (!$objectManager instanceof EntityManagerInterface) {
+            throw new \LogicException('Wrong ObjectManager');
+        }
+
+        return $objectManager;
+    }
+}
diff --git a/src/Task/Product/SimpleProductTask.php b/src/Processor/Resource/ProductVariant/SimpleProductVariantResourceProcessor.php
similarity index 63%
rename from src/Task/Product/SimpleProductTask.php
rename to src/Processor/Resource/ProductVariant/SimpleProductVariantResourceProcessor.php
index bf02634c..ec72df16 100644
--- a/src/Task/Product/SimpleProductTask.php
+++ b/src/Processor/Resource/ProductVariant/SimpleProductVariantResourceProcessor.php
@@ -2,12 +2,13 @@
 
 declare(strict_types=1);
 
-namespace Synolia\SyliusAkeneoPlugin\Task\Product;
+namespace Synolia\SyliusAkeneoPlugin\Processor\Resource\ProductVariant;
 
 use Doctrine\ORM\EntityManagerInterface;
 use LogicException;
 use Psr\Log\LoggerInterface;
 use Sylius\Component\Core\Model\ProductInterface;
+use Sylius\Component\Core\Model\ProductVariantInterface;
 use Sylius\Component\Product\Factory\ProductFactoryInterface;
 use Sylius\Component\Product\Factory\ProductVariantFactoryInterface;
 use Sylius\Component\Resource\Factory\FactoryInterface;
@@ -17,74 +18,41 @@
 use Synolia\SyliusAkeneoPlugin\Event\Product\BeforeProcessingProductEvent;
 use Synolia\SyliusAkeneoPlugin\Event\ProductVariant\AfterProcessingProductVariantEvent;
 use Synolia\SyliusAkeneoPlugin\Event\ProductVariant\BeforeProcessingProductVariantEvent;
-use Synolia\SyliusAkeneoPlugin\Payload\PipelinePayloadInterface;
-use Synolia\SyliusAkeneoPlugin\Payload\Product\ProductPayload;
-use Synolia\SyliusAkeneoPlugin\Processor\Product\ProductChannelEnablerProcessorInterface;
 use Synolia\SyliusAkeneoPlugin\Processor\Product\ProductProcessorChainInterface;
 use Synolia\SyliusAkeneoPlugin\Processor\ProductVariant\ProductVariantProcessorChainInterface;
-use Synolia\SyliusAkeneoPlugin\Repository\ChannelRepository;
-use Synolia\SyliusAkeneoPlugin\Repository\LocaleRepositoryInterface;
+use Synolia\SyliusAkeneoPlugin\Processor\Resource\AkeneoResourceProcessorInterface;
 use Throwable;
 
-final class SimpleProductTask extends AbstractCreateProductEntities
+class SimpleProductVariantResourceProcessor implements AkeneoResourceProcessorInterface, ProductVariantAkeneoResourceProcessorInterface
 {
-    /**
-     * @SuppressWarnings(PHPMD.ExcessiveParameterList)
-     */
     public function __construct(
-        RepositoryInterface $productRepository,
-        ChannelRepository $channelRepository,
-        RepositoryInterface $productVariantRepository,
-        LocaleRepositoryInterface $localeRepository,
-        RepositoryInterface $productConfigurationRepository,
+        private RepositoryInterface $productRepository,
+        private RepositoryInterface $productVariantRepository,
         private FactoryInterface $productFactory,
-        ProductVariantFactoryInterface $productVariantFactory,
-        EntityManagerInterface $entityManager,
-        LoggerInterface $akeneoLogger,
+        private ProductVariantFactoryInterface $productVariantFactory,
+        private EntityManagerInterface $entityManager,
+        private LoggerInterface $akeneoLogger,
         private EventDispatcherInterface $dispatcher,
-        ProductChannelEnablerProcessorInterface $productChannelEnabler,
         private ProductProcessorChainInterface $productProcessorChain,
         private ProductVariantProcessorChainInterface $productVariantProcessorChain,
+        protected RepositoryInterface $channelPricingRepository,
+        protected FactoryInterface $channelPricingFactory,
     ) {
-        parent::__construct(
-            $entityManager,
-            $productVariantRepository,
-            $productRepository,
-            $channelRepository,
-            $localeRepository,
-            $productConfigurationRepository,
-            $productVariantFactory,
-            $akeneoLogger,
-            $productChannelEnabler,
-        );
     }
 
-    /**
-     * @param ProductPayload $payload
-     *
-     * @inheritDoc
-     */
-    public function __invoke(PipelinePayloadInterface $payload, array $resource): void
+    private function getOrCreateSimpleVariant(ProductInterface $product): ProductVariantInterface
     {
-        try {
-            $this->dispatcher->dispatch(new BeforeProcessingProductEvent($resource));
-
-            $product = $this->getOrCreateEntity($resource);
-            $this->productProcessorChain->chain($product, $resource);
+        /** @var ProductVariantInterface $productVariant */
+        $productVariant = $this->productVariantRepository->findOneBy(['code' => $product->getCode()]);
 
-            $this->dispatcher->dispatch(new AfterProcessingProductEvent($resource, $product));
-
-            $this->dispatcher->dispatch(new BeforeProcessingProductVariantEvent($resource, $product));
+        if (!$productVariant instanceof ProductVariantInterface) {
+            $productVariant = $this->productVariantFactory->createForProduct($product);
+            $productVariant->setCode($product->getCode());
 
-            $productVariant = $this->getOrCreateSimpleVariant($product);
-            $this->productVariantProcessorChain->chain($productVariant, $resource);
-
-            $this->dispatcher->dispatch(new AfterProcessingProductVariantEvent($resource, $productVariant));
-
-            $this->entityManager->flush();
-        } catch (Throwable $throwable) {
-            $this->logger->warning($throwable->getMessage(), ['exception' => $throwable]);
+            $this->entityManager->persist($productVariant);
         }
+
+        return $productVariant;
     }
 
     private function getOrCreateEntity(array $resource): ProductInterface
@@ -110,4 +78,30 @@ private function getOrCreateEntity(array $resource): ProductInterface
 
         return $product;
     }
+
+    public function support(array $resource): bool
+    {
+        return null === $resource['parent'];
+    }
+
+    public function process(array $resource): void
+    {
+        try {
+            $this->dispatcher->dispatch(new BeforeProcessingProductEvent($resource));
+
+            $product = $this->getOrCreateEntity($resource);
+            $this->productProcessorChain->chain($product, $resource);
+
+            $this->dispatcher->dispatch(new AfterProcessingProductEvent($resource, $product));
+
+            $this->dispatcher->dispatch(new BeforeProcessingProductVariantEvent($resource, $product));
+
+            $productVariant = $this->getOrCreateSimpleVariant($product);
+            $this->productVariantProcessorChain->chain($productVariant, $resource);
+
+            $this->dispatcher->dispatch(new AfterProcessingProductVariantEvent($resource, $productVariant));
+        } catch (Throwable $throwable) {
+            $this->akeneoLogger->warning($throwable->getMessage(), ['exception' => $throwable]);
+        }
+    }
 }
diff --git a/src/Provider/ChainOptionValuesDataProvider.php b/src/Provider/ChainOptionValuesProcessorProvider.php
similarity index 88%
rename from src/Provider/ChainOptionValuesDataProvider.php
rename to src/Provider/ChainOptionValuesProcessorProvider.php
index afc9b814..6ba4caac 100644
--- a/src/Provider/ChainOptionValuesDataProvider.php
+++ b/src/Provider/ChainOptionValuesProcessorProvider.php
@@ -10,7 +10,7 @@
 use Synolia\SyliusAkeneoPlugin\Processor\ProductOptionValue\OptionValuesProcessorInterface;
 use Traversable;
 
-final class ChainOptionValuesDataProvider implements OptionValuesProcessorProviderInterface
+final class ChainOptionValuesProcessorProvider implements OptionValuesProcessorProviderInterface
 {
     /** @var array<OptionValuesProcessorInterface> */
     private array $optionValuesProcessors;
@@ -20,6 +20,9 @@ public function __construct(Traversable $handlers)
         $this->optionValuesProcessors = iterator_to_array($handlers);
     }
 
+    /**
+     * @throws MissingProductOptionValuesProcessorException
+     */
     public function getProcessor(
         AttributeInterface $attribute,
         ProductOptionInterface $productOption,
diff --git a/src/Provider/AkeneoAttributeDataProvider.php b/src/Provider/Data/AkeneoAttributeDataProvider.php
similarity index 96%
rename from src/Provider/AkeneoAttributeDataProvider.php
rename to src/Provider/Data/AkeneoAttributeDataProvider.php
index 3c026e34..0cbee480 100644
--- a/src/Provider/AkeneoAttributeDataProvider.php
+++ b/src/Provider/Data/AkeneoAttributeDataProvider.php
@@ -2,13 +2,14 @@
 
 declare(strict_types=1);
 
-namespace Synolia\SyliusAkeneoPlugin\Provider;
+namespace Synolia\SyliusAkeneoPlugin\Provider\Data;
 
 use Synolia\SyliusAkeneoPlugin\Builder\Attribute\ProductAttributeValueValueBuilder;
 use Synolia\SyliusAkeneoPlugin\Exceptions\Attribute\MissingLocaleTranslationException;
 use Synolia\SyliusAkeneoPlugin\Exceptions\Attribute\MissingLocaleTranslationOrScopeException;
 use Synolia\SyliusAkeneoPlugin\Exceptions\Attribute\MissingScopeException;
 use Synolia\SyliusAkeneoPlugin\Exceptions\Attribute\TranslationNotFoundException;
+use Synolia\SyliusAkeneoPlugin\Provider\SyliusAkeneoLocaleCodeProvider;
 
 final class AkeneoAttributeDataProvider implements AkeneoAttributeDataProviderInterface
 {
diff --git a/src/Provider/AkeneoAttributeDataProviderInterface.php b/src/Provider/Data/AkeneoAttributeDataProviderInterface.php
similarity index 93%
rename from src/Provider/AkeneoAttributeDataProviderInterface.php
rename to src/Provider/Data/AkeneoAttributeDataProviderInterface.php
index 0c7a03b8..70548e94 100644
--- a/src/Provider/AkeneoAttributeDataProviderInterface.php
+++ b/src/Provider/Data/AkeneoAttributeDataProviderInterface.php
@@ -2,7 +2,7 @@
 
 declare(strict_types=1);
 
-namespace Synolia\SyliusAkeneoPlugin\Provider;
+namespace Synolia\SyliusAkeneoPlugin\Provider\Data;
 
 use Synolia\SyliusAkeneoPlugin\Exceptions\Attribute\MissingLocaleTranslationException;
 use Synolia\SyliusAkeneoPlugin\Exceptions\Attribute\MissingLocaleTranslationOrScopeException;
diff --git a/src/Provider/AkeneoAttributePropertiesProvider.php b/src/Provider/Data/AkeneoAttributePropertiesProvider.php
similarity index 97%
rename from src/Provider/AkeneoAttributePropertiesProvider.php
rename to src/Provider/Data/AkeneoAttributePropertiesProvider.php
index f2dfcc41..9f9d2056 100644
--- a/src/Provider/AkeneoAttributePropertiesProvider.php
+++ b/src/Provider/Data/AkeneoAttributePropertiesProvider.php
@@ -2,7 +2,7 @@
 
 declare(strict_types=1);
 
-namespace Synolia\SyliusAkeneoPlugin\Provider;
+namespace Synolia\SyliusAkeneoPlugin\Provider\Data;
 
 use Akeneo\Pim\ApiClient\AkeneoPimClientInterface;
 
diff --git a/src/Provider/AkeneoReferenceEntityAttributeDataProvider.php b/src/Provider/Data/AkeneoReferenceEntityAttributeDataProvider.php
similarity index 97%
rename from src/Provider/AkeneoReferenceEntityAttributeDataProvider.php
rename to src/Provider/Data/AkeneoReferenceEntityAttributeDataProvider.php
index 280721e8..713f2ca1 100644
--- a/src/Provider/AkeneoReferenceEntityAttributeDataProvider.php
+++ b/src/Provider/Data/AkeneoReferenceEntityAttributeDataProvider.php
@@ -2,12 +2,13 @@
 
 declare(strict_types=1);
 
-namespace Synolia\SyliusAkeneoPlugin\Provider;
+namespace Synolia\SyliusAkeneoPlugin\Provider\Data;
 
 use Synolia\SyliusAkeneoPlugin\Exceptions\Attribute\MissingLocaleTranslationException;
 use Synolia\SyliusAkeneoPlugin\Exceptions\Attribute\MissingLocaleTranslationOrScopeException;
 use Synolia\SyliusAkeneoPlugin\Exceptions\Attribute\MissingScopeException;
 use Synolia\SyliusAkeneoPlugin\Exceptions\Attribute\TranslationNotFoundException;
+use Synolia\SyliusAkeneoPlugin\Provider\SyliusAkeneoLocaleCodeProvider;
 
 final class AkeneoReferenceEntityAttributeDataProvider implements AkeneoReferenceEntityAttributeDataProviderInterface
 {
diff --git a/src/Provider/AkeneoReferenceEntityAttributeDataProviderInterface.php b/src/Provider/Data/AkeneoReferenceEntityAttributeDataProviderInterface.php
similarity index 88%
rename from src/Provider/AkeneoReferenceEntityAttributeDataProviderInterface.php
rename to src/Provider/Data/AkeneoReferenceEntityAttributeDataProviderInterface.php
index 2e81a93a..9d8802d2 100644
--- a/src/Provider/AkeneoReferenceEntityAttributeDataProviderInterface.php
+++ b/src/Provider/Data/AkeneoReferenceEntityAttributeDataProviderInterface.php
@@ -2,7 +2,7 @@
 
 declare(strict_types=1);
 
-namespace Synolia\SyliusAkeneoPlugin\Provider;
+namespace Synolia\SyliusAkeneoPlugin\Provider\Data;
 
 interface AkeneoReferenceEntityAttributeDataProviderInterface
 {
diff --git a/src/Provider/AkeneoReferenceEntityAttributePropertiesProvider.php b/src/Provider/Data/AkeneoReferenceEntityAttributePropertiesProvider.php
similarity index 96%
rename from src/Provider/AkeneoReferenceEntityAttributePropertiesProvider.php
rename to src/Provider/Data/AkeneoReferenceEntityAttributePropertiesProvider.php
index bc5d6f11..a9205e7e 100644
--- a/src/Provider/AkeneoReferenceEntityAttributePropertiesProvider.php
+++ b/src/Provider/Data/AkeneoReferenceEntityAttributePropertiesProvider.php
@@ -2,9 +2,10 @@
 
 declare(strict_types=1);
 
-namespace Synolia\SyliusAkeneoPlugin\Provider;
+namespace Synolia\SyliusAkeneoPlugin\Provider\Data;
 
 use Akeneo\Pim\ApiClient\AkeneoPimClientInterface;
+use Synolia\SyliusAkeneoPlugin\Provider\SyliusAkeneoLocaleCodeProvider;
 
 final class AkeneoReferenceEntityAttributePropertiesProvider
 {
diff --git a/src/Provider/Handler/Task/TaskHandlerProvider.php b/src/Provider/Handler/Task/TaskHandlerProvider.php
new file mode 100644
index 00000000..3f324b81
--- /dev/null
+++ b/src/Provider/Handler/Task/TaskHandlerProvider.php
@@ -0,0 +1,36 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Synolia\SyliusAkeneoPlugin\Provider\Handler\Task;
+
+use Psr\Log\LoggerInterface;
+use Symfony\Component\DependencyInjection\Attribute\TaggedIterator;
+use Synolia\SyliusAkeneoPlugin\Handler\Task\TaskHandlerInterface;
+use Synolia\SyliusAkeneoPlugin\Payload\PipelinePayloadInterface;
+
+class TaskHandlerProvider implements TaskHandlerProviderInterface
+{
+    /**
+     * @param TaskHandlerInterface[] $taskHandlers
+     */
+    public function __construct(
+        private LoggerInterface $akeneoLogger,
+        #[TaggedIterator(TaskHandlerInterface::class)]
+        private iterable $taskHandlers = [],
+    ) {
+    }
+
+    public function provide(PipelinePayloadInterface $pipelinePayload): TaskHandlerInterface
+    {
+        foreach ($this->taskHandlers as $taskHandler) {
+            if ($taskHandler->support($pipelinePayload)) {
+                $this->akeneoLogger->notice($taskHandler::class);
+
+                return $taskHandler;
+            }
+        }
+
+        throw new \LogicException('No supported TaskHandler found');
+    }
+}
diff --git a/src/Provider/Handler/Task/TaskHandlerProviderInterface.php b/src/Provider/Handler/Task/TaskHandlerProviderInterface.php
new file mode 100644
index 00000000..97399c6e
--- /dev/null
+++ b/src/Provider/Handler/Task/TaskHandlerProviderInterface.php
@@ -0,0 +1,13 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Synolia\SyliusAkeneoPlugin\Provider\Handler\Task;
+
+use Synolia\SyliusAkeneoPlugin\Handler\Task\TaskHandlerInterface;
+use Synolia\SyliusAkeneoPlugin\Payload\PipelinePayloadInterface;
+
+interface TaskHandlerProviderInterface
+{
+    public function provide(PipelinePayloadInterface $pipelinePayload): TaskHandlerInterface;
+}
diff --git a/src/Provider/Task/Batch/AssetBatchTaskProvider.php b/src/Provider/Task/Batch/AssetBatchTaskProvider.php
new file mode 100644
index 00000000..957bcb71
--- /dev/null
+++ b/src/Provider/Task/Batch/AssetBatchTaskProvider.php
@@ -0,0 +1,27 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Synolia\SyliusAkeneoPlugin\Provider\Task\Batch;
+
+use Synolia\SyliusAkeneoPlugin\Payload\Asset\AssetPayload;
+use Synolia\SyliusAkeneoPlugin\Payload\PipelinePayloadInterface;
+use Synolia\SyliusAkeneoPlugin\Task\Asset\BatchAssetTask;
+use Synolia\SyliusAkeneoPlugin\Task\BatchTaskInterface;
+
+class AssetBatchTaskProvider implements BatchTaskProviderInterface
+{
+    public function __construct(private BatchAssetTask $task)
+    {
+    }
+
+    public function support(PipelinePayloadInterface $pipelinePayload): bool
+    {
+        return $pipelinePayload instanceof AssetPayload;
+    }
+
+    public function getTask(): BatchTaskInterface
+    {
+        return $this->task;
+    }
+}
diff --git a/src/Provider/Task/Batch/AssociationTypeBatchTaskProvider.php b/src/Provider/Task/Batch/AssociationTypeBatchTaskProvider.php
new file mode 100644
index 00000000..982e289a
--- /dev/null
+++ b/src/Provider/Task/Batch/AssociationTypeBatchTaskProvider.php
@@ -0,0 +1,27 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Synolia\SyliusAkeneoPlugin\Provider\Task\Batch;
+
+use Synolia\SyliusAkeneoPlugin\Payload\Association\AssociationTypePayload;
+use Synolia\SyliusAkeneoPlugin\Payload\PipelinePayloadInterface;
+use Synolia\SyliusAkeneoPlugin\Task\AssociationType\BatchAssociationTypesTask;
+use Synolia\SyliusAkeneoPlugin\Task\BatchTaskInterface;
+
+class AssociationTypeBatchTaskProvider implements BatchTaskProviderInterface
+{
+    public function __construct(private BatchAssociationTypesTask $task)
+    {
+    }
+
+    public function support(PipelinePayloadInterface $pipelinePayload): bool
+    {
+        return $pipelinePayload instanceof AssociationTypePayload;
+    }
+
+    public function getTask(): BatchTaskInterface
+    {
+        return $this->task;
+    }
+}
diff --git a/src/Provider/Task/Batch/AttributeBatchTaskProvider.php b/src/Provider/Task/Batch/AttributeBatchTaskProvider.php
new file mode 100644
index 00000000..0335a385
--- /dev/null
+++ b/src/Provider/Task/Batch/AttributeBatchTaskProvider.php
@@ -0,0 +1,27 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Synolia\SyliusAkeneoPlugin\Provider\Task\Batch;
+
+use Synolia\SyliusAkeneoPlugin\Payload\Attribute\AttributePayload;
+use Synolia\SyliusAkeneoPlugin\Payload\PipelinePayloadInterface;
+use Synolia\SyliusAkeneoPlugin\Task\Attribute\BatchAttributesTask;
+use Synolia\SyliusAkeneoPlugin\Task\BatchTaskInterface;
+
+class AttributeBatchTaskProvider implements BatchTaskProviderInterface
+{
+    public function __construct(private BatchAttributesTask $task)
+    {
+    }
+
+    public function support(PipelinePayloadInterface $pipelinePayload): bool
+    {
+        return $pipelinePayload instanceof AttributePayload;
+    }
+
+    public function getTask(): BatchTaskInterface
+    {
+        return $this->task;
+    }
+}
diff --git a/src/Provider/Task/Batch/BatchTaskProvider.php b/src/Provider/Task/Batch/BatchTaskProvider.php
new file mode 100644
index 00000000..4fc46650
--- /dev/null
+++ b/src/Provider/Task/Batch/BatchTaskProvider.php
@@ -0,0 +1,32 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Synolia\SyliusAkeneoPlugin\Provider\Task\Batch;
+
+use Symfony\Component\DependencyInjection\Attribute\TaggedIterator;
+use Synolia\SyliusAkeneoPlugin\Payload\PipelinePayloadInterface;
+use Synolia\SyliusAkeneoPlugin\Task\BatchTaskInterface;
+
+class BatchTaskProvider
+{
+    /**
+     * @param BatchTaskProviderInterface[] $providers
+     */
+    public function __construct(
+        #[TaggedIterator(BatchTaskProviderInterface::class)]
+        private iterable $providers = [],
+    ) {
+    }
+
+    public function getTask(PipelinePayloadInterface $payload): BatchTaskInterface
+    {
+        foreach ($this->providers as $provider) {
+            if ($provider->support($payload)) {
+                return $provider->getTask();
+            }
+        }
+
+        throw new \LogicException('No batch task found');
+    }
+}
diff --git a/src/Provider/Task/Batch/BatchTaskProviderInterface.php b/src/Provider/Task/Batch/BatchTaskProviderInterface.php
new file mode 100644
index 00000000..a6d6cf2d
--- /dev/null
+++ b/src/Provider/Task/Batch/BatchTaskProviderInterface.php
@@ -0,0 +1,17 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Synolia\SyliusAkeneoPlugin\Provider\Task\Batch;
+
+use Symfony\Component\DependencyInjection\Attribute\AutoconfigureTag;
+use Synolia\SyliusAkeneoPlugin\Payload\PipelinePayloadInterface;
+use Synolia\SyliusAkeneoPlugin\Task\BatchTaskInterface;
+
+#[AutoconfigureTag()]
+interface BatchTaskProviderInterface
+{
+    public function support(PipelinePayloadInterface $pipelinePayload): bool;
+
+    public function getTask(): BatchTaskInterface;
+}
diff --git a/src/Provider/Task/Batch/Payload/PayloadBatchTaskProvider.php b/src/Provider/Task/Batch/Payload/PayloadBatchTaskProvider.php
new file mode 100644
index 00000000..ec883b74
--- /dev/null
+++ b/src/Provider/Task/Batch/Payload/PayloadBatchTaskProvider.php
@@ -0,0 +1,31 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Synolia\SyliusAkeneoPlugin\Provider\Task\Batch\Payload;
+
+use Symfony\Component\DependencyInjection\Attribute\TaggedIterator;
+use Synolia\SyliusAkeneoPlugin\Payload\PipelinePayloadInterface;
+
+class PayloadBatchTaskProvider
+{
+    /**
+     * @param PayloadBatchTaskProviderInterface[] $providers
+     */
+    public function __construct(
+        #[TaggedIterator(PayloadBatchTaskProviderInterface::class)]
+        private iterable $providers = [],
+    ) {
+    }
+
+    public function createBatchPayload(PipelinePayloadInterface $payload): PipelinePayloadInterface
+    {
+        foreach ($this->providers as $provider) {
+            if ($provider->support($payload)) {
+                return $provider->createBatchPayload($payload);
+            }
+        }
+
+        throw new \InvalidArgumentException('');
+    }
+}
diff --git a/src/Provider/Task/Batch/Payload/PayloadBatchTaskProviderInterface.php b/src/Provider/Task/Batch/Payload/PayloadBatchTaskProviderInterface.php
new file mode 100644
index 00000000..45725461
--- /dev/null
+++ b/src/Provider/Task/Batch/Payload/PayloadBatchTaskProviderInterface.php
@@ -0,0 +1,16 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Synolia\SyliusAkeneoPlugin\Provider\Task\Batch\Payload;
+
+use Symfony\Component\DependencyInjection\Attribute\AutoconfigureTag;
+use Synolia\SyliusAkeneoPlugin\Payload\PipelinePayloadInterface;
+
+#[AutoconfigureTag()]
+interface PayloadBatchTaskProviderInterface
+{
+    public function support(PipelinePayloadInterface $pipelinePayload): bool;
+
+    public function createBatchPayload(PipelinePayloadInterface $payload): PipelinePayloadInterface;
+}
diff --git a/src/Provider/Task/Batch/Payload/ProductModelPayloadBatchTaskProvider.php b/src/Provider/Task/Batch/Payload/ProductModelPayloadBatchTaskProvider.php
new file mode 100644
index 00000000..9ee4171f
--- /dev/null
+++ b/src/Provider/Task/Batch/Payload/ProductModelPayloadBatchTaskProvider.php
@@ -0,0 +1,23 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Synolia\SyliusAkeneoPlugin\Provider\Task\Batch\Payload;
+
+use Synolia\SyliusAkeneoPlugin\Payload\PipelinePayloadInterface;
+use Synolia\SyliusAkeneoPlugin\Payload\ProductModel\ProductModelPayload;
+
+class ProductModelPayloadBatchTaskProvider implements PayloadBatchTaskProviderInterface
+{
+    public function createBatchPayload(PipelinePayloadInterface $payload): PipelinePayloadInterface
+    {
+        $commandContext = ($payload->hasCommandContext()) ? $payload->getCommandContext() : null;
+
+        return new ProductModelPayload($payload->getAkeneoPimClient(), $commandContext);
+    }
+
+    public function support(PipelinePayloadInterface $pipelinePayload): bool
+    {
+        return $pipelinePayload instanceof ProductModelPayload;
+    }
+}
diff --git a/src/Provider/Task/Batch/ProductModelBatchTaskProvider.php b/src/Provider/Task/Batch/ProductModelBatchTaskProvider.php
new file mode 100644
index 00000000..5b8b5803
--- /dev/null
+++ b/src/Provider/Task/Batch/ProductModelBatchTaskProvider.php
@@ -0,0 +1,27 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Synolia\SyliusAkeneoPlugin\Provider\Task\Batch;
+
+use Synolia\SyliusAkeneoPlugin\Payload\PipelinePayloadInterface;
+use Synolia\SyliusAkeneoPlugin\Payload\ProductModel\ProductModelPayload;
+use Synolia\SyliusAkeneoPlugin\Task\BatchTaskInterface;
+use Synolia\SyliusAkeneoPlugin\Task\ProductModel\BatchProductModelTask;
+
+class ProductModelBatchTaskProvider implements BatchTaskProviderInterface
+{
+    public function __construct(private BatchProductModelTask $task)
+    {
+    }
+
+    public function support(PipelinePayloadInterface $pipelinePayload): bool
+    {
+        return $pipelinePayload instanceof ProductModelPayload;
+    }
+
+    public function getTask(): BatchTaskInterface
+    {
+        return $this->task;
+    }
+}
diff --git a/src/Provider/Task/Batch/ProductVariantBatchTaskProvider.php b/src/Provider/Task/Batch/ProductVariantBatchTaskProvider.php
new file mode 100644
index 00000000..24847d33
--- /dev/null
+++ b/src/Provider/Task/Batch/ProductVariantBatchTaskProvider.php
@@ -0,0 +1,27 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Synolia\SyliusAkeneoPlugin\Provider\Task\Batch;
+
+use Synolia\SyliusAkeneoPlugin\Payload\PipelinePayloadInterface;
+use Synolia\SyliusAkeneoPlugin\Payload\Product\ProductPayload;
+use Synolia\SyliusAkeneoPlugin\Task\BatchTaskInterface;
+use Synolia\SyliusAkeneoPlugin\Task\Product\BatchProductsTask;
+
+class ProductVariantBatchTaskProvider implements BatchTaskProviderInterface
+{
+    public function __construct(private BatchProductsTask $task)
+    {
+    }
+
+    public function support(PipelinePayloadInterface $pipelinePayload): bool
+    {
+        return $pipelinePayload instanceof ProductPayload;
+    }
+
+    public function getTask(): BatchTaskInterface
+    {
+        return $this->task;
+    }
+}
diff --git a/src/Resources/config/processors.yaml b/src/Resources/config/processors.yaml
index 4a9708f0..7cf602b8 100644
--- a/src/Resources/config/processors.yaml
+++ b/src/Resources/config/processors.yaml
@@ -29,7 +29,7 @@ services:
 
     # ProductOptionValue Processors
     Synolia\SyliusAkeneoPlugin\Provider\OptionValuesProcessorProviderInterface:
-        class: Synolia\SyliusAkeneoPlugin\Provider\ChainOptionValuesDataProvider
+        class: Synolia\SyliusAkeneoPlugin\Provider\ChainOptionValuesProcessorProvider
         arguments:
             - !tagged_iterator { tag: !php/const Synolia\SyliusAkeneoPlugin\Processor\ProductOptionValue\OptionValuesProcessorInterface::TAG_ID, default_priority_method: getDefaultPriority }
 
diff --git a/src/Task/Asset/ProcessAssetTask.php b/src/Task/Asset/ProcessAssetTask.php
index 26bf8d10..060301b5 100644
--- a/src/Task/Asset/ProcessAssetTask.php
+++ b/src/Task/Asset/ProcessAssetTask.php
@@ -4,32 +4,22 @@
 
 namespace Synolia\SyliusAkeneoPlugin\Task\Asset;
 
-use Doctrine\ORM\EntityManagerInterface;
 use Psr\Log\LoggerInterface;
 use Synolia\SyliusAkeneoPlugin\Checker\EditionCheckerInterface;
 use Synolia\SyliusAkeneoPlugin\Logger\Messages;
-use Synolia\SyliusAkeneoPlugin\Manager\ProcessManagerInterface;
 use Synolia\SyliusAkeneoPlugin\Payload\Asset\AssetPayload;
 use Synolia\SyliusAkeneoPlugin\Payload\PipelinePayloadInterface;
-use Synolia\SyliusAkeneoPlugin\Task\AbstractProcessTask;
+use Synolia\SyliusAkeneoPlugin\Task\AkeneoTaskInterface;
+use Synolia\SyliusAkeneoPlugin\Task\TaskHandlerTrait;
 
-final class ProcessAssetTask extends AbstractProcessTask
+final class ProcessAssetTask implements AkeneoTaskInterface
 {
+    use TaskHandlerTrait;
+
     public function __construct(
-        EntityManagerInterface $entityManager,
-        LoggerInterface $akeneoLogger,
-        ProcessManagerInterface $processManager,
-        BatchAssetTask $task,
         private EditionCheckerInterface $editionChecker,
-        string $projectDir,
+        private LoggerInterface $logger,
     ) {
-        parent::__construct(
-            $entityManager,
-            $processManager,
-            $task,
-            $akeneoLogger,
-            $projectDir,
-        );
     }
 
     /**
@@ -57,15 +47,7 @@ public function __invoke(PipelinePayloadInterface $payload): PipelinePayloadInte
         $resources = $payload->getAkeneoPimClient()->getAssetFamilyApi()->all();
 
         $this->handle($payload, $resources);
-        $this->processManager->waitForAllProcesses();
 
         return $payload;
     }
-
-    protected function createBatchPayload(PipelinePayloadInterface $payload): PipelinePayloadInterface
-    {
-        $commandContext = ($payload->hasCommandContext()) ? $payload->getCommandContext() : null;
-
-        return new AssetPayload($payload->getAkeneoPimClient(), $commandContext);
-    }
 }
diff --git a/src/Task/AssociationType/BatchAssociationTypesTask.php b/src/Task/AssociationType/BatchAssociationTypesTask.php
index 47752d8c..ab638c3e 100644
--- a/src/Task/AssociationType/BatchAssociationTypesTask.php
+++ b/src/Task/AssociationType/BatchAssociationTypesTask.php
@@ -4,21 +4,14 @@
 
 namespace Synolia\SyliusAkeneoPlugin\Task\AssociationType;
 
-use Akeneo\Pim\ApiClient\Exception\NotFoundHttpException;
 use Doctrine\DBAL\Result;
 use Doctrine\ORM\EntityManagerInterface;
 use Psr\Log\LoggerInterface;
-use Sylius\Component\Product\Model\ProductAssociationTypeInterface;
-use Sylius\Component\Product\Repository\ProductAssociationTypeRepositoryInterface;
-use Sylius\Component\Resource\Factory\FactoryInterface;
-use Synolia\SyliusAkeneoPlugin\Exceptions\Attribute\ExcludedAttributeException;
-use Synolia\SyliusAkeneoPlugin\Exceptions\Attribute\InvalidAttributeException;
-use Synolia\SyliusAkeneoPlugin\Exceptions\NoAttributeResourcesException;
-use Synolia\SyliusAkeneoPlugin\Exceptions\UnsupportedAttributeTypeException;
 use Synolia\SyliusAkeneoPlugin\Logger\Messages;
 use Synolia\SyliusAkeneoPlugin\Payload\Association\AssociationTypePayload;
 use Synolia\SyliusAkeneoPlugin\Payload\PipelinePayloadInterface;
-use Synolia\SyliusAkeneoPlugin\Provider\SyliusAkeneoLocaleCodeProvider;
+use Synolia\SyliusAkeneoPlugin\Processor\Resource\Attribute\AttributeResourceProcessor;
+use Synolia\SyliusAkeneoPlugin\Processor\Resource\Exception\MaxResourceProcessorRetryException;
 use Synolia\SyliusAkeneoPlugin\Task\AbstractBatchTask;
 use Throwable;
 
@@ -28,10 +21,8 @@ final class BatchAssociationTypesTask extends AbstractBatchTask
 
     public function __construct(
         EntityManagerInterface $entityManager,
-        private LoggerInterface $logger,
-        private FactoryInterface $productAssociationTypeFactory,
-        private ProductAssociationTypeRepositoryInterface $productAssociationTypeRepository,
-        private SyliusAkeneoLocaleCodeProvider $syliusAkeneoLocaleCodeProvider,
+        private LoggerInterface $akeneoLogger,
+        private AttributeResourceProcessor $resourceProcessor,
     ) {
         parent::__construct($entityManager);
     }
@@ -39,94 +30,33 @@ public function __construct(
     /**
      * @param AssociationTypePayload $payload
      *
-     * @throws NoAttributeResourcesException
      * @throws Throwable
      */
     public function __invoke(PipelinePayloadInterface $payload): PipelinePayloadInterface
     {
-        $this->logger->debug(self::class);
-        $this->type = $payload->getType();
-        $this->logger->notice(Messages::createOrUpdate($this->type));
-
-        try {
-            $this->entityManager->beginTransaction();
-
-            $query = $this->getSelectStatement($payload);
-            /** @var Result $queryResult */
-            $queryResult = $query->executeQuery();
-
-            while ($results = $queryResult->fetchAllAssociative()) {
-                foreach ($results as $result) {
-                    /** @var array{code: string, labels: array} $resource */
-                    $resource = json_decode($result['values'], true, 512, \JSON_THROW_ON_ERROR);
-
-                    try {
-                        if (!$this->entityManager->getConnection()->isTransactionActive()) {
-                            $this->entityManager->beginTransaction();
-                        }
-
-                        $productAssociationType = $this->productAssociationTypeRepository->findOneBy(['code' => $resource['code']]);
-                        if (!$productAssociationType instanceof ProductAssociationTypeInterface) {
-                            /** @var ProductAssociationTypeInterface $productAssociationType */
-                            $productAssociationType = $this->productAssociationTypeFactory->createNew();
-                            $this->entityManager->persist($productAssociationType);
-
-                            $productAssociationType->setCode($resource['code']);
-                        }
-
-                        $this->setTranslations($resource['labels'], $productAssociationType);
-
-                        $this->entityManager->flush();
-
-                        if ($this->entityManager->getConnection()->isTransactionActive()) {
-                            $this->entityManager->commit();
-                        }
-
-                        $this->entityManager->clear();
-                        unset($resource, $productAssociationType);
-
-                        $this->removeEntry($payload, (int) $result['id']);
-                    } catch (UnsupportedAttributeTypeException | InvalidAttributeException | ExcludedAttributeException | NotFoundHttpException $throwable) {
-                        $this->removeEntry($payload, (int) $result['id']);
-                    } catch (Throwable $throwable) {
-                        if ($this->entityManager->getConnection()->isTransactionActive()) {
-                            $this->entityManager->rollback();
-                        }
-                        $this->logger->warning($throwable->getMessage());
-                    }
+        $this->akeneoLogger->debug(self::class);
+        $type = $payload->getType();
+        $this->akeneoLogger->notice(Messages::createOrUpdate($type));
+
+        $query = $this->getSelectStatement($payload);
+        /** @var Result $queryResult */
+        $queryResult = $query->executeQuery();
+
+        while ($results = $queryResult->fetchAllAssociative()) {
+            foreach ($results as $result) {
+                /** @var array $resource */
+                $resource = json_decode($result['values'], true);
+
+                try {
+                    $this->resourceProcessor->process($resource);
+                    $this->removeEntry($payload, (int) $result['id']);
+                } catch (MaxResourceProcessorRetryException) {
+                    // Skip the failing line
+                    continue;
                 }
             }
-
-            if ($this->entityManager->getConnection()->isTransactionActive()) {
-                $this->entityManager->commit();
-            }
-        } catch (Throwable $throwable) {
-            if ($this->entityManager->getConnection()->isTransactionActive()) {
-                $this->entityManager->rollback();
-            }
-            $this->logger->warning($throwable->getMessage());
-
-            throw $throwable;
         }
 
         return $payload;
     }
-
-    private function setTranslations(array $labels, ProductAssociationTypeInterface $productAssociationType): void
-    {
-        foreach ($this->syliusAkeneoLocaleCodeProvider->getUsedLocalesOnBothPlatforms() as $usedLocalesOnBothPlatform) {
-            $akeneoLocale = $this->syliusAkeneoLocaleCodeProvider->getAkeneoLocale($usedLocalesOnBothPlatform);
-
-            $productAssociationType->setCurrentLocale($usedLocalesOnBothPlatform);
-            $productAssociationType->setFallbackLocale($usedLocalesOnBothPlatform);
-
-            if (!isset($labels[$akeneoLocale])) {
-                $productAssociationType->setName(sprintf('[%s]', $productAssociationType->getCode()));
-
-                continue;
-            }
-
-            $productAssociationType->setName($labels[$akeneoLocale]);
-        }
-    }
 }
diff --git a/src/Task/AssociationType/ProcessAssociationTypeTask.php b/src/Task/AssociationType/ProcessAssociationTypeTask.php
index 59490c86..9d77220a 100644
--- a/src/Task/AssociationType/ProcessAssociationTypeTask.php
+++ b/src/Task/AssociationType/ProcessAssociationTypeTask.php
@@ -4,29 +4,27 @@
 
 namespace Synolia\SyliusAkeneoPlugin\Task\AssociationType;
 
-use Doctrine\ORM\EntityManagerInterface;
 use Psr\Log\LoggerInterface;
 use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
 use Synolia\SyliusAkeneoPlugin\Event\FilterEvent;
 use Synolia\SyliusAkeneoPlugin\Exceptions\Payload\CommandContextIsNullException;
-use Synolia\SyliusAkeneoPlugin\Manager\ProcessManagerInterface;
 use Synolia\SyliusAkeneoPlugin\Payload\Association\AssociationTypePayload;
 use Synolia\SyliusAkeneoPlugin\Payload\PipelinePayloadInterface;
 use Synolia\SyliusAkeneoPlugin\Provider\Configuration\Api\ApiConnectionProviderInterface;
-use Synolia\SyliusAkeneoPlugin\Task\AbstractProcessTask;
+use Synolia\SyliusAkeneoPlugin\Provider\Handler\Task\TaskHandlerProviderInterface;
+use Synolia\SyliusAkeneoPlugin\Task\AkeneoTaskInterface;
+use Synolia\SyliusAkeneoPlugin\Task\TaskHandlerTrait;
 
-final class ProcessAssociationTypeTask extends AbstractProcessTask
+final class ProcessAssociationTypeTask implements AkeneoTaskInterface
 {
+    use TaskHandlerTrait;
+
     public function __construct(
-        EntityManagerInterface $entityManager,
-        LoggerInterface $akeneoLogger,
-        ProcessManagerInterface $processManager,
-        BatchAssociationTypesTask $task,
         private ApiConnectionProviderInterface $apiConnectionProvider,
         private EventDispatcherInterface $eventDispatcher,
-        string $projectDir,
+        private LoggerInterface $logger,
+        private TaskHandlerProviderInterface $taskHandlerProvider,
     ) {
-        parent::__construct($entityManager, $processManager, $task, $akeneoLogger, $projectDir);
     }
 
     /**
@@ -60,15 +58,7 @@ public function __invoke(PipelinePayloadInterface $payload): PipelinePayloadInte
         );
 
         $this->handle($payload, $page);
-        $this->processManager->waitForAllProcesses();
 
         return $payload;
     }
-
-    protected function createBatchPayload(PipelinePayloadInterface $payload): PipelinePayloadInterface
-    {
-        $commandContext = ($payload->hasCommandContext()) ? $payload->getCommandContext() : null;
-
-        return new AssociationTypePayload($payload->getAkeneoPimClient(), $commandContext);
-    }
 }
diff --git a/src/Task/Attribute/BatchAttributesTask.php b/src/Task/Attribute/BatchAttributesTask.php
index fa2eca29..4d396eb9 100644
--- a/src/Task/Attribute/BatchAttributesTask.php
+++ b/src/Task/Attribute/BatchAttributesTask.php
@@ -4,42 +4,22 @@
 
 namespace Synolia\SyliusAkeneoPlugin\Task\Attribute;
 
-use Akeneo\Pim\ApiClient\Exception\NotFoundHttpException;
 use Doctrine\DBAL\Result;
 use Doctrine\ORM\EntityManagerInterface;
 use Psr\Log\LoggerInterface;
-use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
-use Synolia\SyliusAkeneoPlugin\Creator\AttributeCreatorInterface;
-use Synolia\SyliusAkeneoPlugin\Event\Attribute\AfterProcessingAttributeEvent;
-use Synolia\SyliusAkeneoPlugin\Event\Attribute\BeforeProcessingAttributeEvent;
-use Synolia\SyliusAkeneoPlugin\Exceptions\Attribute\ExcludedAttributeException;
-use Synolia\SyliusAkeneoPlugin\Exceptions\Attribute\InvalidAttributeException;
-use Synolia\SyliusAkeneoPlugin\Exceptions\NoAttributeResourcesException;
-use Synolia\SyliusAkeneoPlugin\Exceptions\UnsupportedAttributeTypeException;
 use Synolia\SyliusAkeneoPlugin\Logger\Messages;
-use Synolia\SyliusAkeneoPlugin\Payload\AbstractPayload;
 use Synolia\SyliusAkeneoPlugin\Payload\Attribute\AttributePayload;
 use Synolia\SyliusAkeneoPlugin\Payload\PipelinePayloadInterface;
-use Synolia\SyliusAkeneoPlugin\Processor\ProductAttribute\ProductAttributeChoiceProcessorInterface;
-use Synolia\SyliusAkeneoPlugin\Processor\ProductAttribute\ProductAttributeTableProcessorInterface;
-use Synolia\SyliusAkeneoPlugin\Processor\ProductOption\ProductOptionProcessorInterface;
-use Synolia\SyliusAkeneoPlugin\Retriever\FamilyRetrieverInterface;
-use Synolia\SyliusAkeneoPlugin\Retriever\FamilyVariantRetrieverInterface;
+use Synolia\SyliusAkeneoPlugin\Processor\Resource\Attribute\AttributeResourceProcessor;
+use Synolia\SyliusAkeneoPlugin\Processor\Resource\Exception\MaxResourceProcessorRetryException;
 use Synolia\SyliusAkeneoPlugin\Task\AbstractBatchTask;
-use Webmozart\Assert\Assert;
 
 final class BatchAttributesTask extends AbstractBatchTask
 {
     public function __construct(
         EntityManagerInterface $entityManager,
-        private LoggerInterface $logger,
-        private ProductAttributeChoiceProcessorInterface $attributeChoiceProcessor,
-        private ProductOptionProcessorInterface $productOptionProcessor,
-        private ProductAttributeTableProcessorInterface $productAttributeTableProcessor,
-        private EventDispatcherInterface $dispatcher,
-        private AttributeCreatorInterface $attributeCreator,
-        private FamilyRetrieverInterface $familyRetriever,
-        private FamilyVariantRetrieverInterface $familyVariantRetriever,
+        private LoggerInterface $akeneoLogger,
+        private AttributeResourceProcessor $resourceProcessor,
     ) {
         parent::__construct($entityManager);
     }
@@ -47,116 +27,33 @@ public function __construct(
     /**
      * @param AttributePayload $payload
      *
-     * @throws NoAttributeResourcesException
      * @throws \Throwable
      */
     public function __invoke(PipelinePayloadInterface $payload): PipelinePayloadInterface
     {
-        $this->logger->debug(self::class);
+        $this->akeneoLogger->debug(self::class);
         $type = $payload->getType();
-        $this->logger->notice(Messages::createOrUpdate($type));
-
-        try {
-            $this->entityManager->beginTransaction();
-
-            $query = $this->getSelectStatement($payload);
-            /** @var Result $queryResult */
-            $queryResult = $query->executeQuery();
-
-            $variationAxes = array_unique($this->getVariationAxes($payload));
-            while ($results = $queryResult->fetchAllAssociative()) {
-                foreach ($results as $result) {
-                    $resource = json_decode($result['values'], true, 512, \JSON_THROW_ON_ERROR);
-
-                    if (!is_array($resource)) {
-                        throw new InvalidAttributeException();
-                    }
-
-                    try {
-                        $this->dispatcher->dispatch(new BeforeProcessingAttributeEvent($resource));
-
-                        if (!$this->entityManager->getConnection()->isTransactionActive()) {
-                            $this->entityManager->beginTransaction();
-                        }
-
-                        $attribute = $this->attributeCreator->create($resource);
-                        $this->entityManager->flush();
-
-                        //Handle attribute options
-                        $this->attributeChoiceProcessor->process($attribute, $resource);
-
-                        //Handle attribute table configuration
-                        $this->productAttributeTableProcessor->process($attribute, $resource);
-
-                        //Handler options
-                        $this->productOptionProcessor->process($attribute, $variationAxes);
-
-                        $this->dispatcher->dispatch(new AfterProcessingAttributeEvent($resource, $attribute));
-
-                        $this->entityManager->flush();
-                        if ($this->entityManager->getConnection()->isTransactionActive()) {
-                            $this->entityManager->commit();
-                        }
-
-                        $this->entityManager->clear();
-                        unset($resource, $attribute);
-
-                        $this->removeEntry($payload, (int) $result['id']);
-                    } catch (UnsupportedAttributeTypeException | InvalidAttributeException | ExcludedAttributeException | NotFoundHttpException $throwable) {
-                        $this->removeEntry($payload, (int) $result['id']);
-                    } catch (\Throwable $throwable) {
-                        if ($this->entityManager->getConnection()->isTransactionActive()) {
-                            $this->entityManager->rollback();
-                        }
-                        $this->logger->warning($throwable->getMessage());
-                    }
+        $this->akeneoLogger->notice(Messages::createOrUpdate($type));
+
+        $query = $this->getSelectStatement($payload);
+        /** @var Result $queryResult */
+        $queryResult = $query->executeQuery();
+
+        while ($results = $queryResult->fetchAllAssociative()) {
+            foreach ($results as $result) {
+                /** @var array $resource */
+                $resource = json_decode($result['values'], true);
+
+                try {
+                    $this->resourceProcessor->process($resource);
+                    $this->removeEntry($payload, (int) $result['id']);
+                } catch (MaxResourceProcessorRetryException) {
+                    // Skip the failing line
+                    continue;
                 }
             }
-
-            if ($this->entityManager->getConnection()->isTransactionActive()) {
-                $this->entityManager->commit();
-            }
-        } catch (\Throwable $throwable) {
-            if ($this->entityManager->getConnection()->isTransactionActive()) {
-                $this->entityManager->rollback();
-            }
-            $this->logger->warning($throwable->getMessage());
-
-            throw $throwable;
         }
 
         return $payload;
     }
-
-    private function getVariationAxes(PipelinePayloadInterface $payload): array
-    {
-        Assert::isInstanceOf($payload, AbstractPayload::class);
-        $variationAxes = [];
-        $families = $this->familyRetriever->getFamilies();
-
-        foreach ($families as $family) {
-            $familyVariants = $this->familyVariantRetriever->getVariants($family['code']);
-
-            $variationAxes = array_merge($variationAxes, $this->getVariationAxesForFamilies($familyVariants));
-        }
-
-        return $variationAxes;
-    }
-
-    private function getVariationAxesForFamilies(array $familyVariants): array
-    {
-        $variationAxes = [];
-
-        /** @var array{variant_attribute_sets: array} $familyVariant */
-        foreach ($familyVariants as $familyVariant) {
-            /** @var array{axes: array} $variantAttributeSet */
-            foreach ($familyVariant['variant_attribute_sets'] as $variantAttributeSet) {
-                foreach ($variantAttributeSet['axes'] as $axe) {
-                    $variationAxes[] = $axe;
-                }
-            }
-        }
-
-        return $variationAxes;
-    }
 }
diff --git a/src/Task/Attribute/ProcessAttributeTask.php b/src/Task/Attribute/ProcessAttributeTask.php
index 127d6d26..4ab442f1 100644
--- a/src/Task/Attribute/ProcessAttributeTask.php
+++ b/src/Task/Attribute/ProcessAttributeTask.php
@@ -4,29 +4,26 @@
 
 namespace Synolia\SyliusAkeneoPlugin\Task\Attribute;
 
-use Doctrine\ORM\EntityManagerInterface;
 use Psr\Log\LoggerInterface;
 use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
 use Synolia\SyliusAkeneoPlugin\Event\FilterEvent;
 use Synolia\SyliusAkeneoPlugin\Exceptions\Payload\CommandContextIsNullException;
-use Synolia\SyliusAkeneoPlugin\Manager\ProcessManagerInterface;
-use Synolia\SyliusAkeneoPlugin\Payload\Attribute\AttributePayload;
 use Synolia\SyliusAkeneoPlugin\Payload\PipelinePayloadInterface;
 use Synolia\SyliusAkeneoPlugin\Provider\Configuration\Api\ApiConnectionProviderInterface;
-use Synolia\SyliusAkeneoPlugin\Task\AbstractProcessTask;
+use Synolia\SyliusAkeneoPlugin\Provider\Handler\Task\TaskHandlerProviderInterface;
+use Synolia\SyliusAkeneoPlugin\Task\AkeneoTaskInterface;
+use Synolia\SyliusAkeneoPlugin\Task\TaskHandlerTrait;
 
-final class ProcessAttributeTask extends AbstractProcessTask
+final class ProcessAttributeTask implements AkeneoTaskInterface
 {
+    use TaskHandlerTrait;
+
     public function __construct(
-        EntityManagerInterface $entityManager,
-        LoggerInterface $akeneoLogger,
-        ProcessManagerInterface $processManager,
-        BatchAttributesTask $task,
         private ApiConnectionProviderInterface $apiConnectionProvider,
         private EventDispatcherInterface $eventDispatcher,
-        string $projectDir,
+        private LoggerInterface $logger,
+        private TaskHandlerProviderInterface $taskHandlerProvider,
     ) {
-        parent::__construct($entityManager, $processManager, $task, $akeneoLogger, $projectDir);
     }
 
     /**
@@ -63,15 +60,7 @@ public function __invoke(PipelinePayloadInterface $payload): PipelinePayloadInte
         );
 
         $this->handle($payload, $page);
-        $this->processManager->waitForAllProcesses();
 
         return $payload;
     }
-
-    protected function createBatchPayload(PipelinePayloadInterface $payload): PipelinePayloadInterface
-    {
-        $commandContext = ($payload->hasCommandContext()) ? $payload->getCommandContext() : null;
-
-        return new AttributePayload($payload->getAkeneoPimClient(), $commandContext);
-    }
 }
diff --git a/src/Task/Option/DeleteTask.php b/src/Task/Option/DeleteTask.php
deleted file mode 100644
index 5e7065e3..00000000
--- a/src/Task/Option/DeleteTask.php
+++ /dev/null
@@ -1,96 +0,0 @@
-<?php
-
-declare(strict_types=1);
-
-namespace Synolia\SyliusAkeneoPlugin\Task\Option;
-
-use Doctrine\ORM\EntityManagerInterface;
-use LogicException;
-use Psr\Log\LoggerInterface;
-use Sylius\Component\Product\Model\ProductOption;
-use Sylius\Component\Product\Model\ProductOptionValueInterface;
-use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
-use Synolia\SyliusAkeneoPlugin\Logger\Messages;
-use Synolia\SyliusAkeneoPlugin\Payload\AbstractPayload;
-use Synolia\SyliusAkeneoPlugin\Payload\PipelinePayloadInterface;
-use Synolia\SyliusAkeneoPlugin\Repository\ProductAttributeRepository;
-use Synolia\SyliusAkeneoPlugin\Repository\ProductOptionRepository;
-use Synolia\SyliusAkeneoPlugin\Task\AkeneoTaskInterface;
-use Throwable;
-
-final class DeleteTask implements AkeneoTaskInterface
-{
-    private string $type;
-
-    private int $deleteCount = 0;
-
-    public function __construct(
-        private EntityManagerInterface $entityManager,
-        private ProductAttributeRepository $productAttributeRepository,
-        private ProductOptionRepository $productOptionRepository,
-        private LoggerInterface $logger,
-        private ParameterBagInterface $parameterBag,
-    ) {
-    }
-
-    /**
-     * @param AbstractPayload $payload
-     */
-    public function __invoke(PipelinePayloadInterface $payload): PipelinePayloadInterface
-    {
-        $this->logger->debug(self::class);
-        $this->logger->notice(Messages::removalNoLongerExist($payload->getType()));
-        $this->type = $payload->getType();
-
-        if (!$this->productAttributeRepository instanceof ProductAttributeRepository) {
-            throw new LogicException('Wrong repository instance provided.');
-        }
-        if (!$this->productOptionRepository instanceof ProductOptionRepository) {
-            throw new LogicException('Wrong repository instance provided.');
-        }
-
-        try {
-            $this->entityManager->beginTransaction();
-
-            $this->process();
-
-            $this->entityManager->flush();
-            $this->entityManager->commit();
-        } catch (Throwable $throwable) {
-            $this->entityManager->rollback();
-            $this->logger->warning($throwable->getMessage());
-
-            throw $throwable;
-        }
-
-        $this->logger->notice(Messages::countOfDeleted($payload->getType(), $this->deleteCount));
-
-        return $payload;
-    }
-
-    private function process(): void
-    {
-        $attributeCodes = $this->productAttributeRepository->getAllAttributeCodes();
-        $removedOptionIds = $this->productOptionRepository->getRemovedOptionIds($attributeCodes);
-
-        /** @var class-string $productOptionClass */
-        $productOptionClass = $this->parameterBag->get('sylius.model.product_option.class');
-        if (!class_exists($productOptionClass)) {
-            throw new LogicException('ProductOption class does not exist.');
-        }
-
-        foreach ($removedOptionIds as $removedOptionId) {
-            /** @var ProductOption $referenceEntity */
-            $referenceEntity = $this->entityManager->getReference($productOptionClass, $removedOptionId);
-            if (null !== $referenceEntity) {
-                /** @var ProductOptionValueInterface $optionValue */
-                foreach ($referenceEntity->getValues() as $optionValue) {
-                    $this->entityManager->remove($optionValue);
-                }
-                $this->entityManager->remove($referenceEntity);
-                $this->logger->info(Messages::hasBeenDeleted($this->type, (string) $referenceEntity->getCode()));
-                ++$this->deleteCount;
-            }
-        }
-    }
-}
diff --git a/src/Task/Product/AbstractCreateProductEntities.php b/src/Task/Product/AbstractCreateProductEntities.php
deleted file mode 100644
index d1fb5b41..00000000
--- a/src/Task/Product/AbstractCreateProductEntities.php
+++ /dev/null
@@ -1,51 +0,0 @@
-<?php
-
-declare(strict_types=1);
-
-namespace Synolia\SyliusAkeneoPlugin\Task\Product;
-
-use Doctrine\ORM\EntityManagerInterface;
-use Psr\Log\LoggerInterface;
-use Sylius\Component\Core\Model\ProductInterface;
-use Sylius\Component\Core\Model\ProductVariantInterface;
-use Sylius\Component\Product\Factory\ProductVariantFactoryInterface;
-use Sylius\Component\Resource\Factory\FactoryInterface;
-use Sylius\Component\Resource\Repository\RepositoryInterface;
-use Synolia\SyliusAkeneoPlugin\Processor\Product\ProductChannelEnablerProcessorInterface;
-use Synolia\SyliusAkeneoPlugin\Repository\ChannelRepository;
-use Synolia\SyliusAkeneoPlugin\Repository\LocaleRepositoryInterface;
-
-abstract class AbstractCreateProductEntities
-{
-    protected RepositoryInterface $channelPricingRepository;
-
-    protected FactoryInterface $channelPricingFactory;
-
-    public function __construct(
-        protected EntityManagerInterface $entityManager,
-        protected RepositoryInterface $productVariantRepository,
-        protected RepositoryInterface $productRepository,
-        protected ChannelRepository $channelRepository,
-        protected LocaleRepositoryInterface $localeRepository,
-        protected RepositoryInterface $productConfigurationRepository,
-        protected ProductVariantFactoryInterface $productVariantFactory,
-        protected LoggerInterface $logger,
-        protected ProductChannelEnablerProcessorInterface $productChannelEnabler,
-    ) {
-    }
-
-    protected function getOrCreateSimpleVariant(ProductInterface $product): ProductVariantInterface
-    {
-        /** @var ProductVariantInterface $productVariant */
-        $productVariant = $this->productVariantRepository->findOneBy(['code' => $product->getCode()]);
-
-        if (!$productVariant instanceof ProductVariantInterface) {
-            $productVariant = $this->productVariantFactory->createForProduct($product);
-            $productVariant->setCode($product->getCode());
-
-            $this->entityManager->persist($productVariant);
-        }
-
-        return $productVariant;
-    }
-}
diff --git a/src/Task/Product/BatchProductsTask.php b/src/Task/Product/BatchProductsTask.php
index 7a84f03a..79d1e3fc 100644
--- a/src/Task/Product/BatchProductsTask.php
+++ b/src/Task/Product/BatchProductsTask.php
@@ -6,57 +6,38 @@
 
 use Doctrine\DBAL\Result;
 use Doctrine\ORM\EntityManagerInterface;
-use Psr\Log\LoggerInterface;
 use Synolia\SyliusAkeneoPlugin\Payload\PipelinePayloadInterface;
-use Synolia\SyliusAkeneoPlugin\Payload\Product\ProductPayload;
+use Synolia\SyliusAkeneoPlugin\Processor\Resource\Exception\MaxResourceProcessorRetryException;
+use Synolia\SyliusAkeneoPlugin\Processor\Resource\ProductVariant\ProductVariantResourceProcessor;
 use Synolia\SyliusAkeneoPlugin\Task\AbstractBatchTask;
-use Throwable;
 
 final class BatchProductsTask extends AbstractBatchTask
 {
     public function __construct(
         EntityManagerInterface $entityManager,
-        private SimpleProductTask $batchSimpleProductTask,
-        private ConfigurableProductsTask $batchConfigurableProductsTask,
-        private LoggerInterface $logger,
+        private ProductVariantResourceProcessor $resourceProcessor,
     ) {
         parent::__construct($entityManager);
     }
 
     public function __invoke(PipelinePayloadInterface $payload): PipelinePayloadInterface
     {
-        if (!$payload instanceof ProductPayload) {
-            return $payload;
-        }
-
         $query = $this->getSelectStatement($payload);
         /** @var Result $queryResult */
         $queryResult = $query->executeQuery();
 
         while ($results = $queryResult->fetchAllAssociative()) {
             foreach ($results as $result) {
-                try {
-                    /** @var array{identifier: string,parent: string|null} $resource */
-                    $resource = json_decode($result['values'], true, 512, \JSON_THROW_ON_ERROR);
+                /** @var array $resource */
+                $resource = json_decode($result['values'], true);
 
-                    $isSimple = null === $resource['parent'];
-                    if ($isSimple) {
-                        $this->logger->debug('Processing Simple Product', [
-                            'parent' => $resource['parent'],
-                            'code' => $resource['identifier'],
-                        ]);
-                        $this->batchSimpleProductTask->__invoke($payload, $resource);
-                    } else {
-                        $this->logger->debug('Processing Configurable Product', [
-                            'parent' => $resource['parent'],
-                            'code' => $resource['identifier'],
-                        ]);
-                        $this->batchConfigurableProductsTask->__invoke($payload, $resource);
-                    }
-
-                    $this->removeEntry($payload, (int) $result['id']);
-                } catch (Throwable) {
+                try {
+                    $this->resourceProcessor->process($resource);
+                    unset($resource);
                     $this->removeEntry($payload, (int) $result['id']);
+                } catch (MaxResourceProcessorRetryException) {
+                    // Skip the failing line
+                    continue;
                 }
             }
         }
diff --git a/src/Task/Product/ProcessProductsTask.php b/src/Task/Product/ProcessProductsTask.php
index 0c11a578..89dbf30d 100644
--- a/src/Task/Product/ProcessProductsTask.php
+++ b/src/Task/Product/ProcessProductsTask.php
@@ -4,43 +4,34 @@
 
 namespace Synolia\SyliusAkeneoPlugin\Task\Product;
 
-use Akeneo\Pim\ApiClient\Pagination\Page;
 use Akeneo\Pim\ApiClient\Pagination\PageInterface;
-use Doctrine\DBAL\ParameterType;
-use Doctrine\ORM\EntityManagerInterface;
 use Psr\Log\LoggerInterface;
 use Symfony\Component\EventDispatcher\EventDispatcherInterface;
 use Synolia\SyliusAkeneoPlugin\Event\FilterEvent;
 use Synolia\SyliusAkeneoPlugin\Exceptions\Payload\CommandContextIsNullException;
 use Synolia\SyliusAkeneoPlugin\Filter\ProductFilterInterface;
 use Synolia\SyliusAkeneoPlugin\Logger\Messages;
-use Synolia\SyliusAkeneoPlugin\Manager\ProcessManagerInterface;
 use Synolia\SyliusAkeneoPlugin\Payload\PipelinePayloadInterface;
 use Synolia\SyliusAkeneoPlugin\Payload\Product\ProductPayload;
 use Synolia\SyliusAkeneoPlugin\Provider\Configuration\Api\ApiConnectionProviderInterface;
-use Synolia\SyliusAkeneoPlugin\Task\AbstractProcessTask;
+use Synolia\SyliusAkeneoPlugin\Provider\Handler\Task\TaskHandlerProviderInterface;
+use Synolia\SyliusAkeneoPlugin\Task\AkeneoTaskInterface;
+use Synolia\SyliusAkeneoPlugin\Task\TaskHandlerTrait;
 
-final class ProcessProductsTask extends AbstractProcessTask
+final class ProcessProductsTask implements AkeneoTaskInterface
 {
+    use TaskHandlerTrait;
+
     public function __construct(
-        EntityManagerInterface $entityManager,
-        LoggerInterface $akeneoLogger,
-        ProcessManagerInterface $processManager,
-        BatchProductsTask $task,
         private ProductFilterInterface $productFilter,
         private ApiConnectionProviderInterface $apiConnectionProvider,
         private EventDispatcherInterface $eventDispatcher,
-        string $projectDir,
+        private LoggerInterface $logger,
+        private TaskHandlerProviderInterface $taskHandlerProvider,
     ) {
-        parent::__construct($entityManager, $processManager, $task, $akeneoLogger, $projectDir);
     }
 
     /**
-     * @SuppressWarnings(PHPMD.NPathComplexity)
-     * @SuppressWarnings(PHPMD.CyclomaticComplexity)
-     *
-     * @TODO Probably need to be refactored
-     *
      * @param ProductPayload $payload
      */
     public function __invoke(PipelinePayloadInterface $payload): PipelinePayloadInterface
@@ -69,89 +60,15 @@ public function __invoke(PipelinePayloadInterface $payload): PipelinePayloadInte
         $queryParameters = array_merge_recursive($queryParameters, $payload->getCustomFilters());
         $this->logger->notice('Filters', $queryParameters);
 
-        /** @var \Akeneo\Pim\ApiClient\Pagination\PageInterface|null $resources */
+        /** @var PageInterface|null $resources */
         $resources = $payload->getAkeneoPimClient()->getProductApi()->listPerPage(
             $this->apiConnectionProvider->get()->getPaginationSize(),
             true,
             $queryParameters,
         );
 
-        if (!$resources instanceof Page) {
-            return $payload;
-        }
-
-        $count = 0;
-        $ids = [];
-
-        $this->handleProducts($payload, $resources, $count, $ids);
-
-        if ($count > 0 && count($ids) > 0 && $payload->isBatchingAllowed() && $payload->getProcessAsSoonAsPossible() && $payload->allowParallel()) {
-            $this->logger->notice('Batching', ['from_id' => $ids[0], 'to_id' => $ids[(is_countable($ids) ? \count($ids) : 0) - 1]]);
-            $this->batch($payload, $ids);
-        }
-
-        if ($count > 0 && count($ids) > 0 && $payload->isBatchingAllowed() && $payload->getProcessAsSoonAsPossible() && !$payload->allowParallel()) {
-            $payload->setIds($ids);
-            $this->task->__invoke($payload);
-        }
-
-        if ($count > 0 && count($ids) > 0 && !$payload->isBatchingAllowed()) {
-            $payload->setIds($ids);
-            $this->task->__invoke($payload);
-        }
-
-        if ($count > 0 && !$payload->getProcessAsSoonAsPossible()) {
-            $this->process($payload);
-        }
-
-        $this->processManager->startAll();
+        $this->handle($payload, $resources);
 
         return $payload;
     }
-
-    private function handleProducts(
-        PipelinePayloadInterface $payload,
-        PageInterface $page,
-        int &$count = 0,
-        array &$ids = [],
-    ): void {
-        $this->processManager->setInstantProcessing($payload->getProcessAsSoonAsPossible());
-        $this->processManager->setNumberOfParallelProcesses($payload->getMaxRunningProcessQueueSize());
-
-        while (
-            ($page instanceof Page && $page->hasNextPage()) ||
-            ($page instanceof Page && !$page->hasPreviousPage()) ||
-            $page instanceof Page
-        ) {
-            foreach ($page->getItems() as $item) {
-                $sql = sprintf(
-                    'INSERT INTO `%s` (`values`, `is_simple`) VALUES (:values, :is_simple);',
-                    ProductPayload::TEMP_AKENEO_TABLE_NAME,
-                );
-
-                $stmt = $this->entityManager->getConnection()->prepare($sql);
-                $stmt->bindValue('values', json_encode($item, \JSON_THROW_ON_ERROR));
-                $stmt->bindValue('is_simple', null === $item['parent'], ParameterType::BOOLEAN);
-                $stmt->execute();
-                ++$count;
-
-                $ids[] = $this->entityManager->getConnection()->lastInsertId();
-
-                if ($payload->getProcessAsSoonAsPossible() && $payload->allowParallel() && 0 === $count % $payload->getBatchSize()) {
-                    $this->logger->notice('Batching', ['from_id' => $ids[0], 'to_id' => $ids[\count($ids) - 1]]);
-                    $this->batch($payload, $ids);
-                    $ids = [];
-                }
-            }
-
-            $page = $page->getNextPage();
-        }
-    }
-
-    protected function createBatchPayload(PipelinePayloadInterface $payload): PipelinePayloadInterface
-    {
-        $commandContext = ($payload->hasCommandContext()) ? $payload->getCommandContext() : null;
-
-        return new ProductPayload($payload->getAkeneoPimClient(), $commandContext);
-    }
 }
diff --git a/src/Task/Product/SetupProductTask.php b/src/Task/Product/SetupProductTask.php
deleted file mode 100644
index bc7ca63d..00000000
--- a/src/Task/Product/SetupProductTask.php
+++ /dev/null
@@ -1,44 +0,0 @@
-<?php
-
-declare(strict_types=1);
-
-namespace Synolia\SyliusAkeneoPlugin\Task\Product;
-
-use Doctrine\ORM\EntityManagerInterface;
-use Synolia\SyliusAkeneoPlugin\Payload\PipelinePayloadInterface;
-use Synolia\SyliusAkeneoPlugin\Payload\Product\ProductPayload;
-use Synolia\SyliusAkeneoPlugin\Provider\TaskProvider;
-use Synolia\SyliusAkeneoPlugin\Task\AkeneoTaskInterface;
-
-final class SetupProductTask implements AkeneoTaskInterface
-{
-    public function __construct(private EntityManagerInterface $entityManager, private TaskProvider $taskProvider)
-    {
-    }
-
-    public function __invoke(PipelinePayloadInterface $payload): PipelinePayloadInterface
-    {
-        if ($payload->isContinue()) {
-            $schemaManager = $this->entityManager->getConnection()->getSchemaManager();
-            $tableExist = $schemaManager->tablesExist([ProductPayload::TEMP_AKENEO_TABLE_NAME]);
-
-            if (true === $tableExist) {
-                return $payload;
-            }
-        }
-
-        $this->taskProvider->get(TearDownProductTask::class)->__invoke($payload);
-
-        $query = sprintf(
-            'CREATE TABLE `%s` (
-              `id` INT NOT NULL AUTO_INCREMENT,
-              `values` JSON NULL,
-              `is_simple` TINYINT NOT NULL,
-              PRIMARY KEY (`id`));',
-            ProductPayload::TEMP_AKENEO_TABLE_NAME,
-        );
-        $this->entityManager->getConnection()->executeStatement($query);
-
-        return $payload;
-    }
-}
diff --git a/src/Task/Product/TearDownProductTask.php b/src/Task/Product/TearDownProductTask.php
deleted file mode 100644
index d3f3cea4..00000000
--- a/src/Task/Product/TearDownProductTask.php
+++ /dev/null
@@ -1,38 +0,0 @@
-<?php
-
-declare(strict_types=1);
-
-namespace Synolia\SyliusAkeneoPlugin\Task\Product;
-
-use Doctrine\DBAL\Exception\ConnectionLost;
-use Doctrine\ORM\EntityManagerInterface;
-use Synolia\SyliusAkeneoPlugin\Payload\PipelinePayloadInterface;
-use Synolia\SyliusAkeneoPlugin\Payload\Product\ProductPayload;
-use Synolia\SyliusAkeneoPlugin\Task\AkeneoTaskInterface;
-
-final class TearDownProductTask implements AkeneoTaskInterface
-{
-    public function __construct(private EntityManagerInterface $entityManager)
-    {
-    }
-
-    public function __invoke(PipelinePayloadInterface $payload): PipelinePayloadInterface
-    {
-        try {
-            $this->delete();
-        } catch (ConnectionLost) {
-            $this->delete();
-        }
-
-        return $payload;
-    }
-
-    private function delete(): void
-    {
-        $exists = $this->entityManager->getConnection()->getSchemaManager()->tablesExist([ProductPayload::TEMP_AKENEO_TABLE_NAME]);
-
-        if ($exists) {
-            $this->entityManager->getConnection()->getSchemaManager()->dropTable(ProductPayload::TEMP_AKENEO_TABLE_NAME);
-        }
-    }
-}
diff --git a/src/Task/ProductModel/BatchProductModelTask.php b/src/Task/ProductModel/BatchProductModelTask.php
index a5a1246d..eaae613a 100644
--- a/src/Task/ProductModel/BatchProductModelTask.php
+++ b/src/Task/ProductModel/BatchProductModelTask.php
@@ -6,43 +6,22 @@
 
 use Doctrine\DBAL\Exception;
 use Doctrine\ORM\EntityManagerInterface;
-use Doctrine\ORM\ORMInvalidArgumentException;
-use Doctrine\Persistence\ManagerRegistry;
 use Psr\Log\LoggerInterface;
-use Sylius\Component\Core\Model\ProductInterface;
-use Sylius\Component\Core\Repository\ProductRepositoryInterface;
-use Sylius\Component\Product\Factory\ProductFactoryInterface;
-use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
-use Synolia\SyliusAkeneoPlugin\Checker\Product\IsProductProcessableCheckerInterface;
-use Synolia\SyliusAkeneoPlugin\Event\Product\AfterProcessingProductEvent;
-use Synolia\SyliusAkeneoPlugin\Event\Product\BeforeProcessingProductEvent;
-use Synolia\SyliusAkeneoPlugin\Logger\Messages;
 use Synolia\SyliusAkeneoPlugin\Payload\PipelinePayloadInterface;
 use Synolia\SyliusAkeneoPlugin\Payload\ProductModel\ProductModelPayload;
-use Synolia\SyliusAkeneoPlugin\Processor\Product\ProductProcessorChainInterface;
-use Synolia\SyliusAkeneoPlugin\Processor\ProductGroup\ProductGroupProcessor;
+use Synolia\SyliusAkeneoPlugin\Processor\Resource\Exception\MaxResourceProcessorRetryException;
+use Synolia\SyliusAkeneoPlugin\Processor\Resource\Product\ProductModelResourceProcessor;
 use Synolia\SyliusAkeneoPlugin\Task\AbstractBatchTask;
 
 final class BatchProductModelTask extends AbstractBatchTask
 {
-    private string $type;
-
     /**
      * @SuppressWarnings(PHPMD.ExcessiveParameterList)
      */
     public function __construct(
         EntityManagerInterface $entityManager,
-        private ProductFactoryInterface $productFactory,
-        private ProductRepositoryInterface $productRepository,
         private LoggerInterface $logger,
-        private EventDispatcherInterface $dispatcher,
-        private ProductProcessorChainInterface $productProcessorChain,
-        private IsProductProcessableCheckerInterface $isProductProcessableChecker,
-        private ProductGroupProcessor $productGroupProcessor,
-        private ManagerRegistry $managerRegistry,
-        private int $maxRetryCount,
-        private int $retryWaitTime,
-        private int $retryCount = 0,
+        private ProductModelResourceProcessor $resourceProcessor,
     ) {
         parent::__construct($entityManager);
     }
@@ -54,126 +33,27 @@ public function __construct(
      */
     public function __invoke(PipelinePayloadInterface $payload): PipelinePayloadInterface
     {
-        if ($this->retryCount === $this->maxRetryCount) {
-            return $payload;
-        }
-
         $this->logger->debug(self::class);
-        $this->type = $payload->getType();
 
         $query = $this->getSelectStatement($payload);
         $queryResult = $query->executeQuery();
 
         while ($results = $queryResult->fetchAllAssociative()) {
             foreach ($results as $result) {
-                $isSuccess = false;
-
                 /** @var array $resource */
                 $resource = json_decode($result['values'], true);
 
-                do {
-                    try {
-                        $this->logger->notice('Processing product', [
-                            'code' => $resource['code'] ?? $resource['identifier'] ?? 'unknown',
-                        ]);
-
-                        $this->handleProductModel($resource);
-                        $isSuccess = true;
-                    } catch (ORMInvalidArgumentException $ormInvalidArgumentException) {
-                        ++$this->retryCount;
-                        usleep($this->retryWaitTime);
-
-                        $this->logger->error('Retrying import', [
-                            'product' => $result,
-                            'retry_count' => $this->retryCount,
-                            'error' => $ormInvalidArgumentException->getMessage(),
-                        ]);
-
-                        $this->entityManager = $this->getNewEntityManager();
-                    } catch (\Throwable $throwable) {
-                        ++$this->retryCount;
-                        usleep($this->retryWaitTime);
-
-                        $this->logger->error('Error importing product', [
-                            'message' => $throwable->getMessage(),
-                            'trace' => $throwable->getTraceAsString(),
-                        ]);
-
-                        $this->entityManager = $this->getNewEntityManager();
-                    }
-                } while (false === $isSuccess && $this->retryCount < $this->maxRetryCount);
-
-                unset($resource);
-                $this->removeEntry($payload, (int) $result['id']);
-                $this->retryCount = 0;
+                try {
+                    $this->resourceProcessor->process($resource);
+                    unset($resource);
+                    $this->removeEntry($payload, (int) $result['id']);
+                } catch (MaxResourceProcessorRetryException) {
+                    // Skip the failing line
+                    continue;
+                }
             }
         }
 
         return $payload;
     }
-
-    private function handleProductModel(array $resource): void
-    {
-        $this->handleProductGroup($resource);
-        $this->dispatcher->dispatch(new BeforeProcessingProductEvent($resource));
-
-        if (!$this->isProductProcessableChecker->check($resource)) {
-            return;
-        }
-
-        $product = $this->process($resource);
-        $this->dispatcher->dispatch(new AfterProcessingProductEvent($resource, $product));
-        $this->entityManager->flush();
-    }
-
-    private function handleProductGroup(array $resource): void
-    {
-        try {
-            $this->productGroupProcessor->process($resource);
-            $this->entityManager->flush();
-        } catch (ORMInvalidArgumentException $ormInvalidArgumentException) {
-            if (!$this->entityManager->isOpen()) {
-                $this->logger->warning('Recreating entity manager', ['exception' => $ormInvalidArgumentException]);
-                $this->entityManager = $this->getNewEntityManager();
-            }
-
-            ++$this->retryCount;
-
-            throw $ormInvalidArgumentException;
-        }
-    }
-
-    private function process(array &$resource): ProductInterface
-    {
-        $product = $this->productRepository->findOneByCode($resource['code']);
-
-        if (!$product instanceof ProductInterface) {
-            /** @var ProductInterface $product */
-            $product = $this->productFactory->createNew();
-            $product->setCode($resource['code']);
-
-            $this->entityManager->persist($product);
-            $this->productProcessorChain->chain($product, $resource);
-
-            $this->logger->info(Messages::hasBeenCreated($this->type, (string) $product->getCode()));
-
-            return $product;
-        }
-
-        $this->productProcessorChain->chain($product, $resource);
-        $this->logger->info(Messages::hasBeenUpdated($this->type, (string) $resource['code']));
-
-        return $product;
-    }
-
-    private function getNewEntityManager(): EntityManagerInterface
-    {
-        $objectManager = $this->managerRegistry->resetManager();
-
-        if (!$objectManager instanceof EntityManagerInterface) {
-            throw new \LogicException('Wrong ObjectManager');
-        }
-
-        return $objectManager;
-    }
 }
diff --git a/src/Task/ProductModel/ProcessProductModelsTask.php b/src/Task/ProductModel/ProcessProductModelsTask.php
index 39789552..684b9054 100644
--- a/src/Task/ProductModel/ProcessProductModelsTask.php
+++ b/src/Task/ProductModel/ProcessProductModelsTask.php
@@ -4,33 +4,31 @@
 
 namespace Synolia\SyliusAkeneoPlugin\Task\ProductModel;
 
-use Doctrine\ORM\EntityManagerInterface;
 use Psr\Log\LoggerInterface;
 use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
 use Synolia\SyliusAkeneoPlugin\Event\FilterEvent;
 use Synolia\SyliusAkeneoPlugin\Exceptions\Payload\CommandContextIsNullException;
 use Synolia\SyliusAkeneoPlugin\Filter\ProductFilter;
 use Synolia\SyliusAkeneoPlugin\Logger\Messages;
-use Synolia\SyliusAkeneoPlugin\Manager\ProcessManagerInterface;
 use Synolia\SyliusAkeneoPlugin\Payload\PipelinePayloadInterface;
 use Synolia\SyliusAkeneoPlugin\Payload\ProductModel\ProductModelPayload;
 use Synolia\SyliusAkeneoPlugin\Provider\Configuration\Api\ApiConnectionProviderInterface;
-use Synolia\SyliusAkeneoPlugin\Task\AbstractProcessTask;
+use Synolia\SyliusAkeneoPlugin\Provider\Handler\Task\TaskHandlerProviderInterface;
+use Synolia\SyliusAkeneoPlugin\Task\AkeneoTaskInterface;
+use Synolia\SyliusAkeneoPlugin\Task\TaskHandlerTrait;
 use Throwable;
 
-final class ProcessProductModelsTask extends AbstractProcessTask
+final class ProcessProductModelsTask implements AkeneoTaskInterface
 {
+    use TaskHandlerTrait;
+
     public function __construct(
-        EntityManagerInterface $entityManager,
-        LoggerInterface $akeneoLogger,
-        ProcessManagerInterface $processManager,
-        BatchProductModelTask $task,
         private ProductFilter $productFilter,
         private ApiConnectionProviderInterface $apiConnectionProvider,
         private EventDispatcherInterface $eventDispatcher,
-        string $projectDir,
+        private LoggerInterface $logger,
+        private TaskHandlerProviderInterface $taskHandlerProvider,
     ) {
-        parent::__construct($entityManager, $processManager, $task, $akeneoLogger, $projectDir);
     }
 
     /**
@@ -71,15 +69,6 @@ public function __invoke(PipelinePayloadInterface $payload): PipelinePayloadInte
 
         $this->handle($payload, $resources);
 
-        $this->processManager->startAll();
-
         return $payload;
     }
-
-    protected function createBatchPayload(PipelinePayloadInterface $payload): PipelinePayloadInterface
-    {
-        $commandContext = ($payload->hasCommandContext()) ? $payload->getCommandContext() : null;
-
-        return new ProductModelPayload($payload->getAkeneoPimClient(), $commandContext);
-    }
 }
diff --git a/src/Task/SetupTask.php b/src/Task/SetupTask.php
index ebda9f24..3cf4b785 100644
--- a/src/Task/SetupTask.php
+++ b/src/Task/SetupTask.php
@@ -4,37 +4,18 @@
 
 namespace Synolia\SyliusAkeneoPlugin\Task;
 
-use Doctrine\ORM\EntityManagerInterface;
 use Synolia\SyliusAkeneoPlugin\Payload\PipelinePayloadInterface;
+use Synolia\SyliusAkeneoPlugin\Provider\Handler\Task\TaskHandlerProviderInterface;
 
 final class SetupTask implements AkeneoTaskInterface
 {
-    public function __construct(private EntityManagerInterface $entityManager, private TearDownTask $tearDownTask)
-    {
+    public function __construct(
+        private TaskHandlerProviderInterface $taskHandlerProvider,
+    ) {
     }
 
     public function __invoke(PipelinePayloadInterface $payload): PipelinePayloadInterface
     {
-        if ($payload->isContinue()) {
-            $schemaManager = $this->entityManager->getConnection()->getSchemaManager();
-            $tableExist = $schemaManager->tablesExist([$payload->getTmpTableName()]);
-
-            if (true === $tableExist) {
-                return $payload;
-            }
-        }
-
-        $this->tearDownTask->__invoke($payload);
-
-        $query = sprintf(
-            'CREATE TABLE `%s` (
-              `id` INT NOT NULL AUTO_INCREMENT,
-              `values` JSON NULL,
-              PRIMARY KEY (`id`));',
-            $payload->getTmpTableName(),
-        );
-        $this->entityManager->getConnection()->executeStatement($query);
-
-        return $payload;
+        return $this->taskHandlerProvider->provide($payload)->setUp($payload);
     }
 }
diff --git a/src/Task/TaskHandlerTrait.php b/src/Task/TaskHandlerTrait.php
new file mode 100644
index 00000000..93b00802
--- /dev/null
+++ b/src/Task/TaskHandlerTrait.php
@@ -0,0 +1,30 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Synolia\SyliusAkeneoPlugin\Task;
+
+use Akeneo\Pim\ApiClient\Pagination\PageInterface;
+use Akeneo\Pim\ApiClient\Pagination\ResourceCursorInterface;
+use Synolia\SyliusAkeneoPlugin\Payload\PipelinePayloadInterface;
+use Synolia\SyliusAkeneoPlugin\Provider\Handler\Task\TaskHandlerProviderInterface;
+
+trait TaskHandlerTrait
+{
+    public function __construct(
+        private TaskHandlerProviderInterface $taskHandlerProvider,
+    ) {
+    }
+
+    protected function process(PipelinePayloadInterface $pipelinePayload): void
+    {
+        $this->taskHandlerProvider->provide($pipelinePayload)->process($pipelinePayload);
+    }
+
+    protected function handle(
+        PipelinePayloadInterface $pipelinePayload,
+        ResourceCursorInterface|PageInterface $handleType,
+    ): void {
+        $this->taskHandlerProvider->provide($pipelinePayload)->handle($pipelinePayload, $handleType);
+    }
+}
diff --git a/src/Task/TearDownTask.php b/src/Task/TearDownTask.php
index 56b06196..0bef4d41 100644
--- a/src/Task/TearDownTask.php
+++ b/src/Task/TearDownTask.php
@@ -4,33 +4,18 @@
 
 namespace Synolia\SyliusAkeneoPlugin\Task;
 
-use Doctrine\DBAL\Exception\ConnectionLost;
-use Doctrine\ORM\EntityManagerInterface;
 use Synolia\SyliusAkeneoPlugin\Payload\PipelinePayloadInterface;
+use Synolia\SyliusAkeneoPlugin\Provider\Handler\Task\TaskHandlerProviderInterface;
 
 final class TearDownTask implements AkeneoTaskInterface
 {
-    public function __construct(private EntityManagerInterface $entityManager)
-    {
+    public function __construct(
+        private TaskHandlerProviderInterface $taskHandlerProvider,
+    ) {
     }
 
     public function __invoke(PipelinePayloadInterface $payload): PipelinePayloadInterface
     {
-        try {
-            $this->delete($payload);
-        } catch (ConnectionLost) {
-            $this->delete($payload);
-        }
-
-        return $payload;
-    }
-
-    private function delete(PipelinePayloadInterface $payload): void
-    {
-        $exists = $this->entityManager->getConnection()->getSchemaManager()->tablesExist([$payload->getTmpTableName()]);
-
-        if ($exists) {
-            $this->entityManager->getConnection()->getSchemaManager()->dropTable($payload->getTmpTableName());
-        }
+        return $this->taskHandlerProvider->provide($payload)->tearDown($payload);
     }
 }
diff --git a/tests/PHPUnit/Provider/AkeneoAttributeDataProviderTest.php b/tests/PHPUnit/Provider/AkeneoAttributeDataProviderTest.php
index 4c80d025..1e285f89 100644
--- a/tests/PHPUnit/Provider/AkeneoAttributeDataProviderTest.php
+++ b/tests/PHPUnit/Provider/AkeneoAttributeDataProviderTest.php
@@ -8,9 +8,9 @@
 use donatj\MockWebServer\Response;
 use Symfony\Component\HttpFoundation\Response as HttpResponse;
 use Synolia\SyliusAkeneoPlugin\Builder\Attribute\ProductAttributeValueValueBuilder;
-use Synolia\SyliusAkeneoPlugin\Provider\AkeneoAttributeDataProvider;
-use Synolia\SyliusAkeneoPlugin\Provider\AkeneoAttributeDataProviderInterface;
-use Synolia\SyliusAkeneoPlugin\Provider\AkeneoAttributePropertiesProvider;
+use Synolia\SyliusAkeneoPlugin\Provider\Data\AkeneoAttributeDataProvider;
+use Synolia\SyliusAkeneoPlugin\Provider\Data\AkeneoAttributeDataProviderInterface;
+use Synolia\SyliusAkeneoPlugin\Provider\Data\AkeneoAttributePropertiesProvider;
 use Synolia\SyliusAkeneoPlugin\Provider\SyliusAkeneoLocaleCodeProvider;
 use Tests\Synolia\SyliusAkeneoPlugin\PHPUnit\Task\Attribute\AbstractTaskTest;
 
@@ -24,7 +24,7 @@ final class AkeneoAttributeDataProviderTest extends AbstractTaskTest
     private const DEFAULT_SCOPE = 'ecommerce';
 
     /** @var AkeneoAttributeDataProviderInterface */
-    private \Synolia\SyliusAkeneoPlugin\Provider\AkeneoAttributeDataProvider $attributeDataProvider;
+    private \Synolia\SyliusAkeneoPlugin\Provider\Data\AkeneoAttributeDataProvider $attributeDataProvider;
 
     protected function setUp(): void
     {
diff --git a/tests/PHPUnit/Provider/AkeneoAttributePropertiesProviderTest.php b/tests/PHPUnit/Provider/AkeneoAttributePropertiesProviderTest.php
index aeeb0ed6..83ab65b2 100644
--- a/tests/PHPUnit/Provider/AkeneoAttributePropertiesProviderTest.php
+++ b/tests/PHPUnit/Provider/AkeneoAttributePropertiesProviderTest.php
@@ -7,7 +7,7 @@
 use Akeneo\Pim\ApiClient\Api\AttributeApi;
 use donatj\MockWebServer\Response;
 use Symfony\Component\HttpFoundation\Response as HttpResponse;
-use Synolia\SyliusAkeneoPlugin\Provider\AkeneoAttributePropertiesProvider;
+use Synolia\SyliusAkeneoPlugin\Provider\Data\AkeneoAttributePropertiesProvider;
 use Tests\Synolia\SyliusAkeneoPlugin\PHPUnit\Task\Attribute\AbstractTaskTest;
 
 /**
@@ -17,7 +17,7 @@
  */
 final class AkeneoAttributePropertiesProviderTest extends AbstractTaskTest
 {
-    private \Synolia\SyliusAkeneoPlugin\Provider\AkeneoAttributePropertiesProvider $attributePropertiesProvider;
+    private \Synolia\SyliusAkeneoPlugin\Provider\Data\AkeneoAttributePropertiesProvider $attributePropertiesProvider;
 
     protected function setUp(): void
     {
diff --git a/tests/PHPUnit/Task/Association/AssociateProductsTaskTest.php b/tests/PHPUnit/Task/Association/AssociateProductsTaskTest.php
index d4e47b90..0125dc39 100644
--- a/tests/PHPUnit/Task/Association/AssociateProductsTaskTest.php
+++ b/tests/PHPUnit/Task/Association/AssociateProductsTaskTest.php
@@ -24,7 +24,7 @@
 use Synolia\SyliusAkeneoPlugin\Payload\Association\AssociationTypePayload;
 use Synolia\SyliusAkeneoPlugin\Payload\Category\CategoryPayload;
 use Synolia\SyliusAkeneoPlugin\Payload\ProductModel\ProductModelPayload;
-use Synolia\SyliusAkeneoPlugin\Provider\AkeneoAttributePropertiesProvider;
+use Synolia\SyliusAkeneoPlugin\Provider\Data\AkeneoAttributePropertiesProvider;
 use Synolia\SyliusAkeneoPlugin\Provider\TaskProvider;
 use Synolia\SyliusAkeneoPlugin\Task\Association\AssociateProductsTask;
 use Synolia\SyliusAkeneoPlugin\Task\ProductModel\ProcessProductModelsTask;
diff --git a/tests/PHPUnit/Task/Product/CreateConfigurableProductEntitiesTaskTest.php b/tests/PHPUnit/Task/Product/CreateConfigurableProductEntitiesTaskTest.php
index 656551c0..dc8b8d0b 100644
--- a/tests/PHPUnit/Task/Product/CreateConfigurableProductEntitiesTaskTest.php
+++ b/tests/PHPUnit/Task/Product/CreateConfigurableProductEntitiesTaskTest.php
@@ -18,7 +18,7 @@
 use Synolia\SyliusAkeneoPlugin\Payload\Category\CategoryPayload;
 use Synolia\SyliusAkeneoPlugin\Payload\Product\ProductPayload;
 use Synolia\SyliusAkeneoPlugin\Payload\ProductModel\ProductModelPayload;
-use Synolia\SyliusAkeneoPlugin\Provider\AkeneoAttributePropertiesProvider;
+use Synolia\SyliusAkeneoPlugin\Provider\Data\AkeneoAttributePropertiesProvider;
 use Synolia\SyliusAkeneoPlugin\Provider\TaskProvider;
 use Synolia\SyliusAkeneoPlugin\Task\Product\ProcessProductsTask;
 use Synolia\SyliusAkeneoPlugin\Task\Product\SetupProductTask;
diff --git a/tests/PHPUnit/Task/Product/CreateSimpleProductEntitiesTaskTest.php b/tests/PHPUnit/Task/Product/CreateSimpleProductEntitiesTaskTest.php
index 744f851d..978a091f 100644
--- a/tests/PHPUnit/Task/Product/CreateSimpleProductEntitiesTaskTest.php
+++ b/tests/PHPUnit/Task/Product/CreateSimpleProductEntitiesTaskTest.php
@@ -17,7 +17,7 @@
 use Sylius\Component\Core\Model\TaxonInterface;
 use Symfony\Component\HttpFoundation\Response as HttpResponse;
 use Synolia\SyliusAkeneoPlugin\Payload\Product\ProductPayload;
-use Synolia\SyliusAkeneoPlugin\Provider\AkeneoAttributePropertiesProvider;
+use Synolia\SyliusAkeneoPlugin\Provider\Data\AkeneoAttributePropertiesProvider;
 use Synolia\SyliusAkeneoPlugin\Provider\TaskProvider;
 use Synolia\SyliusAkeneoPlugin\Repository\ProductAttributeRepository;
 use Synolia\SyliusAkeneoPlugin\Task\Product\ProcessProductsTask;
diff --git a/tests/PHPUnit/Task/ProductGroup/ProcessProductGroupModelTaskTest.php b/tests/PHPUnit/Task/ProductGroup/ProcessProductGroupModelTaskTest.php
index 26afeb6b..5de2d11e 100644
--- a/tests/PHPUnit/Task/ProductGroup/ProcessProductGroupModelTaskTest.php
+++ b/tests/PHPUnit/Task/ProductGroup/ProcessProductGroupModelTaskTest.php
@@ -14,7 +14,7 @@
 use Synolia\SyliusAkeneoPlugin\Entity\ProductConfiguration;
 use Synolia\SyliusAkeneoPlugin\Entity\ProductGroup;
 use Synolia\SyliusAkeneoPlugin\Payload\ProductModel\ProductModelPayload;
-use Synolia\SyliusAkeneoPlugin\Provider\AkeneoAttributePropertiesProvider;
+use Synolia\SyliusAkeneoPlugin\Provider\Data\AkeneoAttributePropertiesProvider;
 use Synolia\SyliusAkeneoPlugin\Provider\TaskProvider;
 use Synolia\SyliusAkeneoPlugin\Task\ProductGroup\ProcessProductGroupModelTask;
 use Synolia\SyliusAkeneoPlugin\Task\ProductModel\ProcessProductModelsTask;
diff --git a/tests/PHPUnit/Task/ProductModel/AddOrUpdateProductModelTaskTest.php b/tests/PHPUnit/Task/ProductModel/AddOrUpdateProductModelTaskTest.php
index 2740adf5..0c708acc 100644
--- a/tests/PHPUnit/Task/ProductModel/AddOrUpdateProductModelTaskTest.php
+++ b/tests/PHPUnit/Task/ProductModel/AddOrUpdateProductModelTaskTest.php
@@ -15,7 +15,7 @@
 use Synolia\SyliusAkeneoPlugin\Entity\ProductConfigurationImageMapping;
 use Synolia\SyliusAkeneoPlugin\Entity\ProductGroup;
 use Synolia\SyliusAkeneoPlugin\Payload\ProductModel\ProductModelPayload;
-use Synolia\SyliusAkeneoPlugin\Provider\AkeneoAttributePropertiesProvider;
+use Synolia\SyliusAkeneoPlugin\Provider\Data\AkeneoAttributePropertiesProvider;
 use Synolia\SyliusAkeneoPlugin\Provider\TaskProvider;
 use Synolia\SyliusAkeneoPlugin\Repository\ProductGroupRepository;
 use Synolia\SyliusAkeneoPlugin\Task\ProductModel\ProcessProductModelsTask;
diff --git a/tests/PHPUnit/Task/ProductModel/EnableDisableProductModelTaskTest.php b/tests/PHPUnit/Task/ProductModel/EnableDisableProductModelTaskTest.php
index 2e267f73..6c99941e 100644
--- a/tests/PHPUnit/Task/ProductModel/EnableDisableProductModelTaskTest.php
+++ b/tests/PHPUnit/Task/ProductModel/EnableDisableProductModelTaskTest.php
@@ -9,7 +9,7 @@
 use Sylius\Component\Core\Model\Product;
 use Symfony\Component\HttpFoundation\Response as HttpResponse;
 use Synolia\SyliusAkeneoPlugin\Payload\ProductModel\ProductModelPayload;
-use Synolia\SyliusAkeneoPlugin\Provider\AkeneoAttributePropertiesProvider;
+use Synolia\SyliusAkeneoPlugin\Provider\Data\AkeneoAttributePropertiesProvider;
 use Synolia\SyliusAkeneoPlugin\Provider\TaskProvider;
 use Synolia\SyliusAkeneoPlugin\Task\ProductModel\ProcessProductModelsTask;
 use Synolia\SyliusAkeneoPlugin\Task\SetupTask;
@@ -22,8 +22,7 @@
  */
 final class EnableDisableProductModelTaskTest extends AbstractTaskTest
 {
-    /** @var AkeneoTaskProvider */
-    private $taskProvider;
+    private TaskProvider $taskProvider;
 
     protected function setUp(): void
     {
diff --git a/tests/PHPUnit/Task/ProductOption/DeleteTaskTest.php b/tests/PHPUnit/Task/ProductOption/DeleteTaskTest.php
deleted file mode 100644
index 320353bb..00000000
--- a/tests/PHPUnit/Task/ProductOption/DeleteTaskTest.php
+++ /dev/null
@@ -1,50 +0,0 @@
-<?php
-
-declare(strict_types=1);
-
-namespace Tests\Synolia\SyliusAkeneoPlugin\PHPUnit\Task\ProductOption;
-
-use Sylius\Component\Product\Model\ProductOption;
-use Synolia\SyliusAkeneoPlugin\Payload\Option\OptionsPayload;
-use Synolia\SyliusAkeneoPlugin\Provider\TaskProvider;
-use Synolia\SyliusAkeneoPlugin\Task\Option\DeleteTask;
-
-/**
- * @internal
- *
- * @coversNothing
- */
-final class DeleteTaskTest extends AbstractTaskTest
-{
-    private ?TaskProvider $taskProvider = null;
-
-    protected function setUp(): void
-    {
-        parent::setUp();
-
-        $this->taskProvider = $this->getContainer()->get(TaskProvider::class);
-    }
-
-    public function testDeleteOptionTask(): void
-    {
-        $optionsPayload = new OptionsPayload($this->createClient());
-
-        /** @var \Sylius\Component\Resource\Factory\FactoryInterface $optionFactory */
-        $optionFactory = $this->getContainer()->get('sylius.factory.product_option');
-        /** @var ProductOption $option */
-        $option = $optionFactory->createNew();
-        $option->setCode('fakeCode');
-        $option->setName('fakeName');
-        $this->manager->persist($option);
-        $this->manager->flush();
-
-        /** @var \Synolia\SyliusAkeneoPlugin\Task\Option\DeleteTask $deleteTask */
-        $deleteTask = $this->taskProvider->get(DeleteTask::class);
-        $deleteTask->__invoke($optionsPayload);
-
-        $productOptionRepository = $this->getContainer()->get('sylius.repository.product_option');
-        /** @var \Sylius\Component\Product\Model\ProductOptionInterface $productOption */
-        $productOption = $productOptionRepository->findOneBy(['code' => 'fakeCode']);
-        $this->assertNull($productOption);
-    }
-}