Czy Value Objects w DDD muszą mieć wszystkie właściwości takie same, żeby to były te same byty? Niżej kod klasy, która definiuje produkt w koszyku klienta i metoda w klasie ShoppingCart
.
public class CartProduct : ValueObject
{
public Guid ProductId { get; protected set; }
public decimal UnitPrice { get; protected set; }
public int Quantity { get; protected set; }
public decimal Tax { get; protected set; } // podatek będzie obliczany za pomocą serwisu domenowego w zależności od produktu i kraju
protected CartProduct(Guid productId, decimal unitPrice, int quantity)
{
ProductId = productId;
UnitPrice = unitPrice;
Quantity = quantity;
}
public static CartProduct Create(Product product, int quantity)
{
if (product == null)
throw new ArgumentNullException();
return new CartProduct(product.Id, product.UnitPrice, quantity);
}
public void ChangeQuantity(int quantity)
=> Quantity += quantity;
protected override IEnumerable<object> GetEqualityComponents()
{
yield return ProductId;
yield return UnitPrice;
yield return Tax;
}
}
public void Add(CartProduct cartProduct)
{
if (cartProduct == null)
throw new ArgumentNullException();
if (_cartProducts.Any(p => p == cartProduct))
{
var existingProduct = _cartProducts.Single(p => p == cartProduct);
existingProduct.ChangeQuantity(cartProduct.Quantity);
return;
}
_cartProducts.Add(cartProduct);
}
- W klasie
CartProduct
mam metodęGetEqualityComponents()
, która mówi po jakich właściwościach mam porównywać produkty. W moim przypadku porównuję po id, cenie i podatku ale pomijam ilość. Czy taki byt można nazwaćValueObjectem
a może powinienem ilość przenieść do koszyka i np. definiować produkty w koszyku jakoDictionary<CartProduct, int>
, gdzie int to ilość? - W DDD walidacja powinna być w modelu czy tak jak w CRUD w warstwie aplikacji? Poniżej kawałek kodu, czy dobrze kombinuję? Czy może powinienem dokonać walidacji od razu na
productDto
w kontrolerze?
public class Code : ValueObject
{
public string Ean { get; protected set; }
protected Code(string ean)
{
Ean = ean;
}
public static Result<Code> Create(string ean)
{
IList<Error> errors = new List<Error>();
if (ean.IsEmpty())
errors.Add(DomainErrors.EanEmpty);
// ...
return !errors.Any() ?
Result<Code>.Ok(new Code(ean)) :
Result<Code>.Fail(errors.ToArray());
}
protected override IEnumerable<object> GetEqualityComponents()
{
yield return Ean;
}
}
public class ProductService
{
public Result AddProduct(ProductDto dto)
{
var eanResult = Code.Create(dto.Ean);
if (!eanResult.Succeeded)
return Result.Fail(eanResult.Errors);
// ...
var product = new Product(dto.Name, dto.UnitPrice, eanResult.Value);
// ...
return Result.Ok();
}
}
to nie jest żaden VO, nie w tej postaci
->to nie jest żaden VO, bynajmniej (nie) w tej postaci
.