领域驱动设计和批处理
Domain Driven Design and batch processing
具有以下结构:
演示级别:
Web 界面、REST API 和命令提示符 - 所有仅调用 OrderService。
应用层:
class OrderService
{
private $em;
private $repository;
private $calculator;
public function __construct(
\Doctrine\ORM\EntityManagerInterface $em;
ClientRepositoryInterface $repository,
cumulativeDiscountCalculator $calculator
} {
$this->em = $em;
$this->repository = $repository;
$this->calculator = $calculator;
}
public function calculateCumulativeDiscount($id)
{
$this->em->beginTransaction();
try {
$client = $this->repository->findClient();
$this->calculator->calculate($client);
$this->em->flush();
$this->em->commit();
} catch (\Exception $e) {
$this->em->rollback();
throw $e;
}
}
}
模型层:
interface ClientInterface
{
public function setDiscount($discount);
}
interface ClientRepositoryInterface
{
public function findClient($id);
public function findClientsByDataRange($from, $to);
}
class cumulativeDiscountCalculator
{
public function calculate(ClientInterface $client)
{
$client->setDiscount(mt_rand(1, 50));
}
}
基础设施层:
PHP 原则 2 - 实现 ClientRepositoryInterface。
我的任务 - 为一组客户执行计算折扣。
(方法ClientRepositoryInterface::findClientsByDataRangereturns收集处理)
问题是我需要处理多达 100,000 条记录。我知道如何在技术上做到这一点,但如何在 DDD 方面做到这一点?出现以下问题:
- 批处理使用哪一层?
- 如何收集操作结果:错误、计算成功的客户端等?
- 在哪里设置事务边界(每 N 个客户端 - 提交并开始一个新事务)?
- 我有大约 10-20 个批处理操作,开发任何结构可能有意义吗?
在我看来,您应该将批处理操作视为您域的一部分,而不仅仅是一些 "trivial" 操作。写下需求,您会发现它也需要一些领域建模。例如。您需要存储有关每个批次 运行 的基本数据(类型、时间、处理的记录数、结果、相关错误等),然后您需要具有预览和安排它们的功能(何时、哪个批次 运行,重新 运行 等)。您可能需要一些工具来监控它们的时间或资源(每个 [=37= 需要多长时间,需要多少内存,等等)。
从你上面提到的我可以想象类像:
- BatchRunner
- 批处理接口
- ClientDiscountBatch { $scheduleDay, $scheduleTime }
- BatchResultEntity { $itemProcessed, $itemErrors, $maxMemory,
$持续时间}
- BatchResultRepository
- ...
然后您的每个批处理操作都将实现 BatchInterface 并将由 BatchRunner 管理,结果将由 BatchResultRepository 等保存。
所有操作都将使用您上面提到的其他域 类,例如。累积折扣计算器。
在事务边界方面,您继续使用现有边界 - 例如。聚合根。每次迭代后,您都会增加结果数或记录错误。
具有以下结构:
演示级别:
Web 界面、REST API 和命令提示符 - 所有仅调用 OrderService。
应用层:
class OrderService { private $em; private $repository; private $calculator; public function __construct( \Doctrine\ORM\EntityManagerInterface $em; ClientRepositoryInterface $repository, cumulativeDiscountCalculator $calculator } { $this->em = $em; $this->repository = $repository; $this->calculator = $calculator; } public function calculateCumulativeDiscount($id) { $this->em->beginTransaction(); try { $client = $this->repository->findClient(); $this->calculator->calculate($client); $this->em->flush(); $this->em->commit(); } catch (\Exception $e) { $this->em->rollback(); throw $e; } } }
模型层:
interface ClientInterface { public function setDiscount($discount); } interface ClientRepositoryInterface { public function findClient($id); public function findClientsByDataRange($from, $to); } class cumulativeDiscountCalculator { public function calculate(ClientInterface $client) { $client->setDiscount(mt_rand(1, 50)); } }
基础设施层:
PHP 原则 2 - 实现 ClientRepositoryInterface。
我的任务 - 为一组客户执行计算折扣。 (方法ClientRepositoryInterface::findClientsByDataRangereturns收集处理)
问题是我需要处理多达 100,000 条记录。我知道如何在技术上做到这一点,但如何在 DDD 方面做到这一点?出现以下问题:
- 批处理使用哪一层?
- 如何收集操作结果:错误、计算成功的客户端等?
- 在哪里设置事务边界(每 N 个客户端 - 提交并开始一个新事务)?
- 我有大约 10-20 个批处理操作,开发任何结构可能有意义吗?
在我看来,您应该将批处理操作视为您域的一部分,而不仅仅是一些 "trivial" 操作。写下需求,您会发现它也需要一些领域建模。例如。您需要存储有关每个批次 运行 的基本数据(类型、时间、处理的记录数、结果、相关错误等),然后您需要具有预览和安排它们的功能(何时、哪个批次 运行,重新 运行 等)。您可能需要一些工具来监控它们的时间或资源(每个 [=37= 需要多长时间,需要多少内存,等等)。
从你上面提到的我可以想象类像:
- BatchRunner
- 批处理接口
- ClientDiscountBatch { $scheduleDay, $scheduleTime }
- BatchResultEntity { $itemProcessed, $itemErrors, $maxMemory, $持续时间}
- BatchResultRepository
- ...
然后您的每个批处理操作都将实现 BatchInterface 并将由 BatchRunner 管理,结果将由 BatchResultRepository 等保存。
所有操作都将使用您上面提到的其他域 类,例如。累积折扣计算器。
在事务边界方面,您继续使用现有边界 - 例如。聚合根。每次迭代后,您都会增加结果数或记录错误。