【问题标题】:Where do long running, stateful 'services' fit in DDD?长时间运行的、有状态的“服务”在 DDD 中的位置是什么?
【发布时间】:2014-03-06 07:58:09
【问题描述】:

在更多与行业或自动化相关的应用程序(主要依赖于他们必须管理的外部组件)中,您通常会遇到这样的情况,即域包含的模型不仅仅是对实际问题的抽象,还包括物理上存在于域之外的事物的表示和指针。

例如,以这个代表网络设备的域实体为例:

public class NetworkDevice {
   public IPAddress IpAddress { get; set; }
}

应用程序可能需要根据外部组件在域内的表示来管理外部组件,而不是仅仅存储或验证此类实体或对实体更改采取行动。现在,DDD 甚至适用于这种情况吗?这些经理是域服务吗?

Eric Evans 在他著名的蓝皮书中描述了域服务需要是一个无状态模型,实现从 ubiquitos 语言中获取的方法来满足实体的请求,或者存储库无法自行处理。但是如果服务需要有状态呢?

一个简单的例子:一个应用程序需要监控网络内的配置 IP 设备,以便通知域内的其他应用程序有关状态事件的信息。如果 IP 设备在应用程序中注册(例如存储在数据库中),“ping-service”会收到通知并开始监控设备。

public class PingMonitor : IDisposable,
   IHandle<DeviceRegisteredEvent>,
   IHandle<DeviceRemovedEvent> 
{

    public List<NetworkDevice> _devices = new List<NetworkDevice>();

    public void Handle(DeviceRegisteredEvent @event) {
        _devices.Add(@event.Device);
    }

    public void Handle(DeviceRemovedEvent @event) {
        _devices.Remove(@event.Device);
    }

    public void PingWorker() {
        foreach(var device in _devices) {
            var status = Ping(device.IpAddress);
            if(status != statusBefore)
                DomainEvents.Raise<DeviceStateEvent>(new DeviceStateEvent(device, status));
        }
    }

}

然后其他组件可以处理这些状态事件,例如如果设备离线,请停止通过其他协议与设备通信。

现在,这些组件是什么?起初我认为它们是域服务,因为它们服务于域的某种需求。但是,它们是有状态的,并且不具体代表 ubiquitos 语言ping-service 的任务是 ping 域实体并报告它的状态,但是 ping-service 没有实现客户端允许 ping 设备的方法)。

它们是应用程序服务吗?这些组件在 DDD 模式中的位置是什么?

【问题讨论】:

  • 我不明白为什么 PingMonitor 是有状态的。它处于什么状态?
  • @Hippoom PingMonitor 存储并监视在整个应用程序生命周期内已在应用程序内注册的所有网络设备,因此在单例生命周期范围内运行。

标签: c# design-patterns domain-driven-design cqrs onion-architecture


【解决方案1】:

在 DDD 中,一个长时间运行的进程称为 Saga。它通常使用领域事件来实现。

以下是对该主题的一些介绍: http://abdullin.com/journal/2010/9/26/theory-of-cqrs-command-handlers-sagas-ars-and-event-subscrip.html/

【讨论】:

    【解决方案2】:

    我曾经实现过一个类似的功能,希望对你有帮助:)

    我们的组织拥有一个在线支付处理应用程序。客户完成付款后,在线支付提供商会向用户发送指示成功或失败的通知。有时会发生网络故障,通知可能永远不会到达我们的应用程序。因此,心怀不满的顾客来了。所以需要一个自动检查机制。

    应用程序运行器负责保持检查运行:

    public class CheckingBatch {
        private TransactionChecker transactionChecker;
    
        public void run() {
            List<Transaction> transactions = transactionsToBeChecked();
            for (Transaction transaction : transactions) {
                    //publish events if the transaction needs check
                    doCheck(transaction, now);                }
            } 
        }
    
        private List<Transaction> transactionsToBeChecked() {
             return transactionRepository.findBy(transactionChecker
                .aToBeCheckedSpec());
        }
    }
    

    另一个应用程序服务监听事件并进行实际检查:

    public class CheckTransactionServiceImpl implements CheckTransactionService {
        private TransactionChecker transactionChecker;
    
        @Transactional
        public void check(final TransactionNo transactionNo) {
            Transaction transaction = transactionRepository.find(transactionNo);
            CheckResult result = transactionChecker.check(transaction);
            //handle check result
        }
    }
    

    TransactionCheck 是与在线支付解决方案无关的域服务:

    public interface TransactionChecker {
    /**
     * 
     * | data between online-payment provider and ours | txn STATUS | RESULT |<br>
     * | consistent | CLOSED | VALID |<br>
     * | inconsistent | CLOSED | INVALID |<br>
     * others omitted
     */
        CheckResult check(Transaction transaction);
    /**
     * returns txn specification to filter to be checked ones.
     */
        ToBeCheckedSpecification aToBeCheckedSpec();
    }
    

    如您所见,应用服务和域服务现在都是无状态的。

    恕我直言,Ping 是域服务(与 TxnChecker 相关),但监视器是一种应用程序服务(与 CheckingBatch 相关)。

    【讨论】:

    • 这是一个很好的答案,实际上解决了我在应用程序中遇到的一些其他问题(域/服务层混合)。非常感谢您!
    • @Marco 谢谢,我同意你的看法。但是这个应用程序是在我知道 CQRS 之前构建的 :-(。我认为它对于没有 CQRS 块支持的应用程序仍然有用。
    • @Hippoom,sagas 与 CQRS 没有直接关系,它们更多地属于事件 SOA 领域。 Sagas 有一个特定的用途,所以你的代码确实可以完成这项工作,我只是想指出 Sagas 应该是与 ddd/event soa 一起使用的正确模式。
    猜你喜欢
    • 1970-01-01
    • 2016-04-11
    • 2014-03-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-06-04
    • 1970-01-01
    相关资源
    最近更新 更多