【问题标题】:ddd - Where should synchronization with remote API go?ddd - 与远程 API 的同步应该去哪里?
【发布时间】:2017-01-24 09:43:03
【问题描述】:

为了让我的应用程序正常工作,我需要定期同步来自外部服务的数据(可以是 API,也可以是简单的文本文件,但目前它是一个 API)。

由于这需要一次创建/更新许多实体,我需要创建一个域服务。但是,我还需要创建一些包含远程 API 响应的 DTO 对吗?

这个逻辑应该去哪里?我应该有以下目录结构:

Domain -
    Model - // my set of entities and repository interfaces are here
        ....
    Synchronization -
        RunSynchronizationService.php // domain service
Application
    Synchronization - 
        SynchronizeData.php // application service
        SynchronizationDataSourceInterface.php // used by application service
        MySpecificRemoteApiDataSource.php // this implements the interface above
        SynchronizationDataSourceResponse.php // this would be returned by each call of  SynchronizationDataSourceInterface method, and would contain data normalized, but not validated.
Infrastructure -
    MyConcreteImplementationOfModelEntityRepository.php   

当我想同步数据时,我只需调用 Application\Synchronization\SynchronizeData 的同步方法,它将采用 SynchronizationDataSourceInterface 的具体实现,调用其方法,并在将返回的 SynchronizationDataSourceResponse 对象传输到 Domain\Model\ 之前对其进行验证同步\运行同步服务?

或者我应该删除 RunSynchronizationService(域服务)并让应用程序服务 (SynchronizeData.php) 在同步过程的每个步骤中创建/更新域实体?

【问题讨论】:

  • 我可能错了,但我在这里闻到了一些过度设计的味道。我的第一个建议是在一个(子)包中隔离同步。我的第二个建议是,为什么不从一个小服务开始,等你更好地了解它的角色后让它发展。
  • @IhorBurlachenko 问题是同步过程比较大。我必须从 API 中为每个实体获取并验证 5 到 10 个响应,其中至少有 700 个
  • 然后我会隐藏同步(子)包中的所有细节。我不会在域层中放置任何东西。如果我理解正确,同步与域无关,而只是您的应用程序处理数据的方式。
  • 同步其实是提供数据让应用工作的方式,不过我猜是一样的
  • 我认为 Ihor 可能试图传达的信息(如果我错了,请原谅我,Ihor),您应该将该服务视为可以用竞争供应商替换的小部件。因此,隔离包,但定义域的交互点在结构上比服务本身提供的内容更重要。我通常会在别处(例如通过应用层)创建外部服务实例。然后,我将通过在域或公共区域中定义的接口将该服务传递给需要它的域操作。

标签: php service domain-driven-design


【解决方案1】:

一般来说,当提出关于外部服务接口应该存在于何处的问题时,我会尝试将其视为另一个存储库。我在这里选择的抽象级别将在很大程度上取决于谁将使用该服务(只是这个项目的域/应用程序层?其他项目?),如果服务有不同的版本/供应商,以及如何使用哪个服务确定。

对于您的示例,我将假设此应用程序是唯一直接使用同步服务的应用程序。共享服务需要进一步隔离公共端点(例如接口和输出对象),以避免将不必要的对象溢出到其他项目中。

但在这种情况下,要将服务视为另一个存储库,我将为其放置接口和域期望并在域中使用的标准输出。

您的描述中的“验证”是什么意思有点模糊,所以我将尝试在这里攻击不同的观点。一个或全部都可以申请。

如果验证需要您与域数据进行比较,它可能应该驻留在RunSynchronizationService.php 模块中。该模块似乎负责获取同步数据并将其应用于您的域。

如果验证是针对从同步服务调用返回的数据进行的,并且不需要直接访问域对象图,我会将该验证放在接口的服务实现上,并在服务接口。要处理同步服务的多个版本(例如 VersionA、VersionB 等)中验证相同的情况,您可以使用继承和覆盖,或同步服务的常用帮助函数等。在下面的示例中,我使用了继承。

您可能需要同时进行这两种验证。首先检查与域无关的同步数据问题(在已实现的类上),然后检查域中的业务规则(在RunSynchronizationService 中)。但很可能这两个调用都会在RunSynchronizationService 中发生,因为您会在接口上公开同步数据验证调用。

应用层应负责创建服务实例(MySpecificRemoteApiDataSource),并将其作为SynchronizationDataSourceInterface 传递到RunSynchronizationService.php 模块。如果有多个版本,应用层可能会负责选择哪一个(也许从配置中),并使用工厂。

但这又在很大程度上取决于该同步服务的范围。如果您有依赖它的外部项目,您可能希望服务层本身的工厂部分,以便其他每个项目都使用相同的选择方法。

Domain -
    Model - // my set of entities and repository interfaces are here
        ....
    Synchronization -
        RunSynchronizationService.php // domain service
        SynchronizationDataSourceInterface.php // used to define the contract associated with a sync service
        SynchronizationDataSourceResponse.php // this would be returned by each call of  SynchronizationDataSourceInterface method, and would contain data normalized, but not validated.
Application -  
    Synchronization - 
        SynchronizeData.php // application service - Uses a factory or some means of determining which version to use and introduce the domain to the data point.

Infrastructure -
    MyConcreteImplementationOfModelEntityRepository.php  

Synchornization -
    VersionA -  
        MySpecificRemoteApiDataSource.php  // Implements SynchronizationDataSourceInterface.php, inherits from SyncApiDataSourceBase.php
    SyncApiDataSourceBase.php  // Common logic for sync goes here, such as validation.

【讨论】:

  • 我几乎也做了同样的事情!除了我把 ??SynchronizationDatasourceInterface 的具体实现放在Infrastructure/Synchronization 中。不过,这是一个完全有效的答案,谢谢!
猜你喜欢
  • 2017-03-27
  • 2018-06-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-12-20
  • 1970-01-01
  • 1970-01-01
  • 2020-06-08
相关资源
最近更新 更多