域模型的可配置显示属性

Configurable Display Properties for Domain Models

使用 DDD 并遵循干净的架构模式,我对为特定域模型 ID 配置显示属性的理想位置感到有点困惑。这听起来令人困惑,我想我可以用一个例子来最好地解释它:

这里域模型的业务逻辑很简单:根据输入、增益和偏移量计算 "scaled" 值。

//Domain Model
public class Transducer
{
    //Name is the ID
    public string Name { get; set; }
    public double Gain { get; set; }
    public double Offset { get; set; }
    public double RawValue { get; set; }

    public double ScaledValue { get; private set; }


    public double CalculateScaledValue(double RawValue)
    {
        ScaledValue = (Gain * RawValue) + Offset;
        return ScaledValue;
    }

}

我们有一个用例来协调用户操作与域模型并管理持久性。这里的细节并不重要,所以我只包含了一个示例界面:

//implementation of execution of business logic and persistance would go in the implentation, details left out for this example
public interface ITransducerUseCase
{
    IEnumerable<string> GetAllTransducerNames();
    void AddNewTransducer(string Name, double Gain, double Offset);
    void SetGain(string Name, double Gain);
    void SetOffset(string Name, double Offset);
    void SetRawValue(string Name, double Raw);

    double GetScaledValue(string Name);

}

控制器使用用例来协调用例与视图或其他控制器。这个特定的控制器允许查看所有换能器名称并可以更改它们的增益 属性.

public class Controller
{
    ITransducerUseCase _TransducerUseCase;

    //these are sent to the view to be displayed
    public Dictionary<string, double> _transducerScaledValues = new Dictionary<string, double>();

    public Controller(ITransducerUseCase TransducerUseCase)
    {
        _TransducerUseCase = TransducerUseCase;
        //Get all the names and populate the dictionary to display.
        foreach (var transducerName in _TransducerUseCase.GetAllTransducerNames())
            _transducerScaledValues.Add(transducerName, _TransducerUseCase.GetScaledValue(transducerName));
    }

    //bound to the view
    public string SelectedName { get; set; }
    //bound to the view, a property for setting a new gain value
    public double Gain { get; set; }

    public void OnButtonClick()
    {
        //update the gain
        _TransducerUseCase.ChangeGain(SelectedName, Gain);
        //get the new scaled value            
        _transducerScaledValues[SelectedName] = _TransducerUseCase.GetScaledValue("PumpPressure");

    }
}

这就是这个问题的脚手架。这是新要求:

我想到的解决方案: 在域模型中放置一个 属性 并通过层将其返回到视图。这似乎不是一个合乎逻辑的地方,因为 DisplayRounding 属性 与业务逻辑没有任何关联。

public class Transducer
{
    //This seems like an SRP violation
    public int DisplayRounding { get; set; }

    //Name is the ID
    public string Name { get; set; }
    public double Gain { get; set; }
    public double Offset { get; set; }

    public double ScaledValue { get; private set; }


    public double CalculateScaledValue(double RawValue)
    {
        ScaledValue = (Gain * RawValue) + Offset;
        return ScaledValue;
    }

}

如果不在那里,那么在哪里?

我们可以把它放在一个没有任何业务逻辑的单独的域模型中吗?持久性可以由相同的用例 class 或单独的用例来管理。

public class TransducerDisplaySettings
{
    public int Rounding { get; set; }
    //plus other related properties
} 

优点:它比使用一个组合模型更好地分离问题。

缺点:该模型没有任何业务逻辑,这样可以吗?


我们还考虑过使用某种服务在外层完全管理这些设置。

优点:没有业务逻辑就没有领域模型

缺点:可能会绑定到特定框架?


还有没有pros/cons我不见了?一种方法明显优于另一种方法吗?有没有我完全错过的方法?谢谢!

[table("NameTable")]
public class Transducer
{
    //Name is the ID
    [Key] //is Key from table
    public string Name { get; set; }
    public double Gain { get; set; }
    public double Offset { get; set; }
    public double RawValue { get; set; }

    public double ScaledValue { get; private set; }


    public double CalculateScaledValue(double RawValue)
    {
        ScaledValue = (Gain * RawValue) + Offset;
        return ScaledValue;
    }

}

您必须做出的核心决定是显示舍入是应用程序业务逻辑的一个方面还是 "just an aspect of display"。

如果您认为它对您的业务逻辑很重要,它应该与您的实体一起建模。

如果您将其视为 "presenting values to the user" 的一个方面(因此与业务规则无关),则应将其存储在单独的存储库或服务中,然后由 "presenter" 应用。