DDD 从外部 API、存储库和域服务构建 AggregateRoot 实体

DDD construct AggregateRoot Entity from external API, Repositories and Domain Services

尝试将 DDD 原则应用于小型项目...我有 PlayerProfile 个聚合根,它由 Club 个实体和 Rating 个值对象的集合组成。我必须定期从外部门户同步所有 PlayerProfile 的实体,解析原始 HTML.

现在我想出了包装代码的解决方案,它以简单的 PlayerProfileRepository 更新 PlayerProfile,像这样:

public interface IPlayerProfileRepository
{
    Task<IReadOnlyCollection<PlayerProfile>> SyncPlayersProfilesFromPortal(string sourceUrl);
    // other methods, which works with data storage
}

首先,我不太喜欢混合方法的想法,它使用数据存储和方法,使用外部资源(HTML 页)定期创建 PlayerProfile。对我来说,这听起来更像是 PlayerProfileFactory 责任?

IPlayerProfileRepository 的实际实现将实际页面的解析委托给 3 个 IPageParser,它们实际上与我的存储库位于同一层。像这样:

    public PlayerProfileRepository(
        IPageParser<ParseClubDto> clubPageParser,
        IPageParser<ParsePlayerProfileDto> playerProfilePageParser,
        IPageParser<ParseRatingDto> ratingPageParser)

    {
        _playerProfilePageParser = playerProfilePageParser;
        _clubPageParser = clubPageParser;
    }

我不太确定所有这些 Dtos 是否实际上是 Dtos,因为它们仅在 IPageParser 中用于在解析页面时保存中间数据。我想让它们与数据服务层中的 IPageParser 实现保持紧密联系,但不会在单独的 Dtos 项目中共享它们,并且可能会以不同的方式命名。

ParseClubDtoParsePlayerProfileDtoParseRatingDto解析后,我把它传给了PlayerProfileFactory.Create工厂方法,像这样:

var playerProfiles = new List<PlayerProfile>();

var clubs = await _clubPageParser.ParseAsync(sourceUrl);

foreach (var club in clubs)
{
    var clubPlayers = await _playerProfilePageParser.ParseAsync(club.PlayersPageUrl);

    foreach (var clubPlayer in clubPlayers)
    {
        var ratings = await _ratingPageParser.ParseAsync(clubPlayer.RatingsPageUrl);

        playerProfiles.Add(PlayerProfileFactory.Create(club, clubPlayer, ratings));
     }
}

return playerProfiles;

完成后,我必须与数据库中现有的聚合根执行实际同步,我通过调用聚合根上的 ResyncFrom(PlayerProfile profile) 来简单地进行同步,或者它应该更像是单独的 PlayerProfile 域服务?

总的来说,我觉得我做错了什么,所以欢迎大家提出意见?

我认为您的示例是使用反腐败层模式在两个 BC 之间进行集成的案例。

我会有一个带有方法契约的端口(域中的接口),returns 玩家资料聚合列表。

在基础设施层,我将有一个适配器通过从远程门户读取 html 数据(例如使用 REST API )并从该数据构建聚合来实现端口.

在应用层,我将有一个应用程序服务,您可以在其中注入处理本地数据库的端口和播放器配置文件聚合存储库。应用服务调用端口构建聚合,然后调用存储库存储它们。

我会定期运行此应用程序服务。

这将是一个没有事件的异步集成,但如果远程门户触发事件,您可以使用事件来实现它。

IPlayerProfileRepository 接口通常在域中定义,并向外界描述应该如何检索聚合根,通常通过 Id。所以 SyncPlayersProfilesFromPortal 方法当然不应该是这个接口的一部分。 同步数据是一个基础设施问题,可以在后台异步完成,正如上一个答案中已经建议的那样。