Uno de los mayores problemas que suelen tener las aplicaciones, y que a menudo pasa desapercibido, es la realización de la misma operación repetidamente dentro de un bucle.
Cómo cada operación dura muy poco y la base de datos no se “queja”, suele pasar desapercibido a la hora de monitorizar el rendimiento.
Si usas Sentry, lo tienes en el monitor de N+1 Queries.
Voy a poner un ejemplo sencillo.
Vamos a mostrar un listado de pedidos de un cliente, y tenemos un método que devuelve un Dto que usaremos en el API.
<aside> 💡 NOTA: Alguno de vosotros, vais a pensar que no tenéis este problema, por que montáis una mega Query o hacéis Eager Loading, etc.. Este tipo de soluciones no escalan bien en proyectos grandes. Si tu opinión es diferente, te aconsejo que no sigas leyendo.
</aside>
Por un lado tenemos el Dto que vamos a devolver, que tiene datos del pedido y del cliente.
final readonly class OrderListItemDto{
public function __construct(
public OrderId $orderId,
public string $orderNumber,
public string $orderStatus,
public string $orderDate,
public string $customerName,
public string $customerEmail
){
}
}
Ahora tenemos un servicio que devuelve el listado de pedidos de un cliente:
final readonly class OrderListService
{
public function getOrderList(UserId $userId): array
{
/** @var array<OrderListItemDto> $result */
$result = [];
$orders = $this->orderRepository->findByUserId($userId);
foreach ($orders as $order) {
$result[] = $this->buildDto($order);
}
return $result;
}
private function buildDto(Order $order): OrderListItemDto
{
$customer = $this->customerRepository->findByUserId($order->userId);
return new OrderListItemDto(
orderId: $order->orderId,
orderNumber: $order->orderNumber,
orderStatus: $order->orderStatus,
orderDate: $order->orderDate,
customerName: $customer->customerName,
customerEmail: $customer->customerEmail
);
}
}