如何验证原始参数传递给域服务



说我有一些代码,例如下面的代码:

public IEnumerable<KeyValuePair<int, int>> CalculateDenominationsFor(int cost) 
    {
        var target = cost;
        foreach (var denomination in currency.AvailableDenominations.OrderByDescending(a => a))
        {
           var numberRequired = target / denomination;
           if (numberRequired >= 1)
           {
               yield return new KeyValuePair<int, int>(denomination, numberRequired);
           }
           target = target - (numberRequired * denomination); 
        }
    } 

这是一个域服务。我将如何验证成本参数?我是否必须将其封装在值对象中,然后将验证放入值对象构造函数?

中?

例如,我想确保成本大于零,并且只有我与Sterling合作时有两个小数。

我是否必须将其封装在值对象中,然后将验证放入值对象构造函数?

这是理想的选择,因为无论使用该域对象的其他地方,您都知道它不在无效状态下,因为它的构造函数使得这是不可能的。

作为一个非常粗略的例子,它看起来像这样:

public class Cost
{
    public Cost(decimal amount)
    {
        var rounded = decimal.Round(amount, 2);
        if(rounded <= 0m)
            throw new ArgumentOutOfRangeException(nameof(amount), $"{nameof(amount)} must be greater than zero when rounded to two decimal places.");
        Amount = rounded;
    }
    public decimal Amount { get; }
}

您如何进行围绕的细节可能会有所不同。但关键是,它发生在从您的域外部收到值时进行的,并确保您的Cost类的每个实例在使用其他地方都有效,并且是不可能的。

也可能值得使用用于Money类型的许多Nuget软件包之一,而不是使用decimal。这样,如果价格是特定货币,您可以明确。而且,如果您处理多种货币,您将不会头痛地调整您的代码。

您还可以实现IComparable<Cost>IEquatable<Cost>,以便您可以直接比较Cost的实例,而不必比较其Amount


这只是我使用的金钱类型的片段。nuget,github

public struct Money : IComparable<Money>, IComparable, IXmlSerializable
{
    public Money(decimal amount, Currency currency)
    {
        Amount = amount;
        Currency = currency;
    }
    public decimal Amount { get; private set; }
    public Currency Currency { get; private set; }
    public static Money Round(Money m)
    {
        return new Money(decimal.Round(m.Amount), m.Currency);
    }
}

如果您正在使用类似的东西,那么您的 Cost可能看起来像这样:

public class Cost
{
    public Cost(Money amount)
    {
        if(amount.Currency != Currency.GBP)
            throw new ArgumentException("The currency type must be GPB.");
        var rounded = Money.Round(amount, 2);
        if(rounded.Amount <= 0m)
            throw new ArgumentOutOfRangeException(nameof(amount), $"{nameof(amount)} must be greater than zero when rounded to two decimal places.");
        Amount = rounded;
    }
    public Money Amount { get; }
}

最新更新