域驱动设计:服务和聚合中的域规则

时间:2012-09-10 19:47:41

标签: domain-driven-design business-rules

当涉及多个聚合时,我怀疑域应该如何强制执行业务规则。

假设我有帐户和外部帐户聚合:

public class Account {
    public String getId() {...}
    public void add (Double amount) {}
}

public class ExternalAccount {
    public String getId() {...}
    public void add (Double amount) {}
}

和这项服务:

public class TransferService implements TransferServiceInterface {
    public void transfer (String AccountId, String ExternalAccountId, Double amount) {
        Account fromAccount = accRepository.get(AccountId);
        ExternalAccount toAccount = extAccRepository.get(ExternalAccountId);

        transferIsValid(fromAccount, toAccount, amount);

        fromAccount.add(-amount);
        toAccount.add(amount);
    }
}
如果转移不符合域规则,

transferIsValid将抛出异常。

如何防止此模型的用户不使用服务并执行以下操作:

    Account fromAccount = accRepository.get(AccountId);
    ExternalAccount toAccount = extAccRepository.get(ExternalAccountId);

    fromAccount.add(-amount);
    toAccount.add(amount);

用户未使用该服务,并且未使用transferIsValid(...)来检查完整性。我相信我的设计中存在错误,因为用户不应该做无效的事情。我该怎样预防呢?我的设计错误在哪里?

2 个答案:

答案 0 :(得分:4)

首先:不要使用Add()撤回。 DDD就是关注域名。当您与产品所有者交谈时,我不认为您说So when I add a negative amount of money to account A, the equal amount will be added to account B。添加Widthdraw方法。


记住。编码时不涉及用户。程序员是。所有程序员都可以搞砸代码。

关于服务:您无法通过代码来防止这种情况。除非唯一有效的提款方式是将其转移到另一个账户。在这种情况下,您可以更改Widthdraw()方法以将另一个帐户作为参数。

除此之外,只需在Widthdraw方法中添加文档,并说如果涉及两个帐户就应该使用该服务。 imho任何DDD开发人员应该知道应该使用该服务,因为我们在DDD中做事情(你和我做了,所以下一个开发者也应该有DDD经验)。

答案 1 :(得分:1)

业务逻辑应该在域对象中,因此,我认为更好的方法是避免业务逻辑泄漏到服务,而不是将业务逻辑放在TransferService中。名为AccountTransfer的实体包含AccountFromAccountTo,类似于(抱歉我在这里使用C#):

public class AccountTransfer
{
    Account From { get; set; }
    Account To { get; set; }

    // More properties         

    private bool IsValid(ammount)
    {}

    public void DoTransfer(int amount)
    {
        is (IsValid(ammount))
        {
            From.Withdraw(amount);
            To.Add(amount);
        }
    }
}

您可能需要对象AccountTransfer中的更多信息,例如:

  1. 何时转移
  2. 什么样的转移:通过签证转账,PayPal ....
  3. 要将此类填充到数据库中,请存储传输历史记录以便以后跟踪它们。
  4. 通过这种方式,您还可以将AccountTransfer中的IsValid方法作为私有方法。