Sto progettando BC per i codici promozionali. Funzionano così: L'amministratore può creare il codice promozionale specificando
- Dettagli (come codice e descrizione)
- Benefit (interfaccia per ValueObjects, ad esempio MoneyDiscountBenefit)
-
Limitazioni d'uso - raccolta di AbstractUsageRestriction, ciascuna limitare l'uso del codice promozionale, ad esempio:
- PromoCode può essere utilizzato solo dall'utente sigle
- Il codice promozionale può essere utilizzato su massimo 3 articoli dell'ordine da ciascun utente
- Il codice promozionale può essere utilizzato su un massimo di 10 articoli dell'ordine
- Il codice promozionale può essere utilizzato solo per alcuni OrderItem (alcuni tipi specifici, o dove il prezzo è superiore a qualche limite ecc.)
Il Cliente può applicare questo codice promozionale sul suo Ordine (alcuni VO in questo BC), quindi viene applicato su tutti gli OrderItem che soddisfano tutte le restrizioni. Quindi, in caso di evento OrderPaid, PromoCode viene "utilizzato" su ogni restrizione di articoli da articolo. "Usato", perché questo BC dal punto di vista del cliente si limita a gestire il codice promozionale come applicato / usato e quindi il successivo controllo delle restrizioni (da parte di chiunque su questo codice promozionale) durante l'applicazione / utilizzo può essere influenzato da questo uso (se desiderato), ad esempio con UniqueUsageRestriction se un utente usa un codice promozionale, un altro non può più - la restrizione genera eccezioni.
Ora, la mia entità ha questo aspetto:
<?php
class PromoCode
{
/** @var PromoCodeId */
private $id;
/** @var Details */
private $details;
/** @var Benefit */
private $benefit;
/** @var UsageRestrictions */
private $usageRestrictions;
/** @var PromoCodeUsesCollection */
private $promoCodeUsesCollection;
/** @var PromoCodeAppliesCollection */
private $promoCodeAppliesCollection;
public function __construct(PromoCodeId $id, Details $details, Benefit $benefit, ?UsageRestrictions $usageRestrictions = null)
{
$this->id = $id;
$this->details = $details;
$this->benefit = $benefit;
$this->usageRestrictions = $usageRestrictions ?: new UsageRestrictions();
$this->promoCodeUsesCollection = new PromoCodeUsesCollection();
$this->promoCodeAppliesCollection = new PromoCodeAppliesCollection();
}
public function getId(): PromoCodeId
{
return $this->id;
}
public function getBenefit(): Benefit
{
return $this->benefit;
}
public function getOrderItemIdsOnWhichWasAppliedWith(OrderId $orderId){
$this->promoCodeAppliesCollection->getOrderItemIdsWith($orderId);
}
public function applyFor(Order $order): void
{
$this->checkApplicabilityForOrder($order);
foreach ($order->getOrderItems() as $orderItem) {
$this->checkApplicabilityForOrderItem($order, $orderItem);
$promoCodeApply = new PromoCodeApply($order, $orderItem);
$this->promoCodeAppliesCollection->addApply($promoCodeApply);
}
}
public function useFor(Order $order): void
{
$this->checkApplicabilityForOrder($order);
foreach ($order->getOrderItems() as $orderItem) {
$this->checkApplicabilityForOrderItem($order, $orderItem);
$promoCodeUse = new PromoCodeUse($order, $orderItem);
$this->promoCodeUsesCollection->addUse($promoCodeUse);
}
}
public function countAppliesForOrder(OrderId $orderId): int
{
return $this->promoCodeAppliesCollection->countAppliesForOrder($orderId);
}
public function countOrdersWithUseByUser(UserId $userId): int
{
return $this->promoCodeUsesCollection->countOrdersWithUseByUser($userId);
}
public function canBeUniqueForUser(UserId $userId): bool
{
return $this->promoCodeUsesCollection->canBeUniqueFor($userId);
}
public function hasCode($code)
{
return $this->details->getCode()->getValue() === $code;
}
private function checkApplicabilityForOrder(Order $order): void
{
$this->usageRestrictions->checkApplicabilityForOrder($this, $order);
}
private function checkApplicabilityForOrderItem(Order $order, OrderItem $orderItem): void
{
$this->usageRestrictions->checkApplicabilityForOrderItem($this, $order, $orderItem);
}
}
Ho la sensazione che sappia e faccia troppo, ma come dividerlo?
Non mi piace l'uso e l'applicazione delle funzioni in qualche modo, forse dovrebbe fare qualche strategia? Quindi lo rifasero su qualcosa come:
public function applyFor(Order $order): void
{
$this->applyPromoCodeStrategy->applyForOrder($this, $order, $this->usageRestrictions, $this->promoCodeAppliesCollection)
}
Ma non mi piace il fatto che sto passando a) quattro parametri, b) Quella strategia ora fa qualcosa con lo stato di promoCodeAppliesCollection
proprietà senza che PromoCode lo sappia.
È in qualche modo strano che PromoCode risponde a domande come countOrdersWithUseByUser o canBeUniqueForUser ecc. Solo per riempire le domande di alcune restrizioni specifiche in UsageRestrictions. Avrei bisogno di creare un oggetto mutabile nell'entità PromoCode (con quelle raccolte con applica e usi) che non avrebbe alcun id, e gestivo tutte le cose intorno usando e controllando le restrizioni separatamente, ma non ho visto qualcosa di simile ovunque .
Quali approcci posso utilizzare per migliorare questo design? O puoi nominare qualche odore di design con possibili soluzioni qui?