Mvvm 域模型和;“业务逻辑”;混乱

Mvvm 域模型和;“业务逻辑”;混乱,mvvm,architecture,domain-driven-design,business-logic,domain-model,Mvvm,Architecture,Domain Driven Design,Business Logic,Domain Model,每当我读到一篇关于MVVM或DDD等现代设计模式的文章时,我就很难将这个例子翻译成我通常正在研究的领域 所有这些模式得出的结论是,域模型应该存在于它们自己的小气泡中,没有任何引用,不应该暴露于绑定视图中,应该是POCOs/POJO,并且包含“业务逻辑” 我经常问自己的问题是:域模型应该做什么 答案显然是“处理业务逻辑”,但当我思考这可能是什么时,我很难找到真实世界的例子 例如:经常出现的一个典型示例是金融应用程序,其中您可以有一个银行账户实体,该实体可以有一个TransferMoneyTo(ot

每当我读到一篇关于MVVM或DDD等现代设计模式的文章时,我就很难将这个例子翻译成我通常正在研究的领域

所有这些模式得出的结论是,域模型应该存在于它们自己的小气泡中,没有任何引用,不应该暴露于绑定视图中,应该是POCOs/POJO,并且包含“业务逻辑”

我经常问自己的问题是:域模型应该做什么

答案显然是“处理业务逻辑”,但当我思考这可能是什么时,我很难找到真实世界的例子

例如:经常出现的一个典型示例是金融应用程序,其中您可以有一个
银行账户
实体,该实体可以有一个
TransferMoneyTo(otherAccount)
功能。 这在理论上听起来不错,但在现实世界中,此应用程序不会管理世界上所有的银行帐户,而只管理一家银行的帐户。 因此,现实世界中的应用程序必须以某种方式联系另一家银行的服务器来启动此事务。这个“不知何故”显然是服务,不允许
银行账户
引用该服务。这意味着对于孤立域模型来说,这不是一个很好的例子

到目前为止,我读到的所有例子都是这样的,其中的例子只起作用,因为它忽略了重要的细节或琐碎的东西。我的意思是,“业务逻辑”只是由简单的验证(例如必填字段)组成

所有这些都导致了一个错误(也许除了验证),这被认为是一件坏事

我的问题是:除了验证之外,“业务逻辑”一词背后隐藏着什么,这将证明需要一个单独的域模型

注意:我知道这取决于您所从事的领域,但我认为至少有一些DDD实际有用的例子是值得赞赏的

“业务逻辑”一词背后隐藏着什么

许多域模型反映业务流程,因此包含状态机,您可以根据一些规则将事物从已知的有效状态转换到另一个状态。几乎每一个企业都有这样的流程。其他领域可能涉及更复杂的内部算法和数据转换

<>这些几乎不属于简单的“公正验证”范畴,除非你把铁路公司的座位预订系统或政府的税务计算过程视为“验证”。 这种“不知何故”的服务显然不是银行账户提供的 允许引用

关于与外界交流的领域,这并不是他们真正的责任。通常情况下,您所拥有的是域发出一个事件,说“这发生了!”,应用程序上下文处理该事件并启动与外部系统的适当通信


协调对内部和外部子系统的调用,以便数据流入、流出和流经应用程序不是域逻辑,而是技术应用程序级别的问题。以一种或另一种形式(事件、DI等)的控制反转通常是保持域不知道这一点的关键。

我有一些用php编写的足球比赛管理软件(不完全是oop的顶峰)

我的业务逻辑包括一个排名计算器,它根据比赛结果计算出一支球队的排名。打破僵局的策略可能有点复杂。其他业务逻辑包括比赛安排、裁判分配和志愿者协调

这个逻辑位于我认为是领域层的地方。我的实体本身(游戏等)往往贫血。除了保存数据,他们没什么可做的。但我没意见。真正的工作来自服务类

我使用或使用有界上下文和聚合根概念。我的游戏实体包括团队、官员和场地。当我处于游戏环境中时,游戏就是王者,并且负责它的孩子们。在球场管理环境中,球场是老板,而比赛只是顺其自然

我实现了坚持和独立。我可以对域实体和值对象进行建模,以反映域需求,而不必担心它们最终会出现在什么表中。ORM层负责映射,允许我跨多个表存储一个域实体,反之亦然

关键是,从DDD中挑选有助于您的特定应用程序的内容。其余的不用担心

这个“不知何故”显然是
银行账户
不支持的服务 允许有对的引用。这意味着这将不会是一场灾难 一个孤立域模型的很好的例子

虽然
BankAccount
本身没有对此服务的引用,但它仍然可以与此类服务交互

举一个更简单的例子,我们来计算利息。天真的方法可能是:

public BankAccount 
{
    public decimal Balance { get; set; }
    public decimal Interest { get; set; }
    private public List<Transaction> transactions = new List<Transaction>();
    public List<Transaction> Transactions { get { return transactions; } } 

    public decimal CalculateInterest() 
    {
        return Balance * Interest;
    }
}

// inside a service
BankAccount account = ...;
var interest = account.CalculateInterest();
account.Balance += interest;
account.AddTransaction(new Transaction() { Description = "Monthly Interest", Amount = interest });
现在,您的
BankAccount
类只有一项职责,即维护其状态和所需的业务逻辑(即检查是否有足够的余额,只允许通过方法操纵银行帐户,而不是直接更改
余额
或操纵
列表

计算由计算器类完成,这些类被传递到
BankAccount
CalculateInterest
方法。该服务包含所需的逻辑,既不适用于计算器类,也不适用于BankAccount类

因此,简而言之:业务逻辑(在富域模型中)是类维护其状态并尽可能多地封装它所需的所有逻辑。在第二个类中,不可能直接更改平衡。或者
AddTransac
public BankAccount 
{
    // private setters, so no one outside BankAccount can update it directly
    public decimal Balance { get; private set; }
    public AccountType AccountType { get; private set; } // assume business and private account
    private public List<Transaction> transactions = new List<Transaction>();
    // return as "AsEnumerable" so user can't later cast it back to list and
    // directly add Transactions, skipping the AddTransaction method
    public IEnumerable<Transaction> Transactions { get { return transactions.AsEnumerable(); } } 

    public void CalculateInterest(IInterestCalculator calc) 
    {
        decimal interest = calc.CalculateInterest(this);
        this.AddTransaction(new Transaction() { Description = "Monthly Interest", Amount = interest });
    }

    public void AddTransaction(Transaction transaction) 
    {
        var newBalance = this.Balance + transaction.Balance;

        if(this.transaction.Amount < 0 && newBalance < this.Limit) 
        {
            // new balance would exceed the accounts limit
            throw new NotEnoughFundsException();
        }

        this.transactions.Add(transaction);
        this.Balance = newAmount;
    }
}

public interface IInterestCalculator 
{
    decimal CalculateInterest(Bankaccount);
}

public class DefaultAccountInterestCalculator : IInterestCalculator
{
    public decimal CalculateInterest(BankAccount account) 
    {
        // for sake of simplicity, inlined
        return account.Balance * 0.02;
    }
}
public class PremiumAccountInterestCalculator : IInterestCalculator 
{
    private const decimal Threshold = 10000m;
    public decimal CalculateInterest(BankAccount account) 
    {
        // Premium customers receive increased interest, above a certain threshold. 3% for the balance above the threshold of 10000 USD
        if(account.Balance > Threshold) 
        {
            return (decimal)((Threshold * 0.02) + (account.Balance-Threshold) * 0.03);
        } 
        else 
        {
            return (decimal)(account.Balance * 0.02);
        }
    }
}
BankAccount account = ...;
IInterestCalculator calculator = (account.AccountType == AccountType.Premium)?new PremiumAccountInterestCalculator():DefaultAccountInterestCalculator();

BankAccount account.CalculateInterest(calculator);