Search code examples
c#domain-driven-designddd-repositories

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


Trying to apply DDD principles to small project... I have PlayerProfile aggregate root, which consists of Club entity and collection of Rating value objects. Periodically I have to sync all PlayerProfile's entities from external portal, parsing the raw HTML.

For now I come up with the solution to wrap the code, which renew the PlayerProfile's in simple PlayerProfileRepository, something like this:

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

First, I don't really like the idea of mixing method, which work with data storage with the methods, which work with external resource (HTML pages) to periodically create PlayerProfile. For me it sounds more like PlayerProfileFactory responsibilities?

The actual implementation of IPlayerProfileRepository delegates parsing of actual pages to 3 IPageParser's, which actually lives in the same layer as my repositories do. Something like this:

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

    {
        _playerProfilePageParser = playerProfilePageParser;
        _clubPageParser = clubPageParser;
    }

I am not quite sure if all these Dtos are in fact Dtos as soon as they are used only from IPageParser's to save intermediate data while parsing the pages. I would like to keep them closely to IPageParser implementations in data service layer, but not to share them in separate Dtos project and maybe named differently.

After ParseClubDto, ParsePlayerProfileDto and ParseRatingDto parsed, I passed it to PlayerProfileFactory.Create factory method, something like this:

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;

After this is done I have to perform actual syncing with existing agreggate roots in DB, which I do simple by calling ResyncFrom(PlayerProfile profile) on aggregate root or should it be more like separate PlayerProfile domain service?

In general I got a feeling that I am doing something wrong, so please any comments are welcomed?


Solution

  • I think that your example is a case of integration between two BCs using the anti corruption layer pattern.

    I would have a port (interface in the domain) with a method contract that returns a list of player profile aggregates.

    In the infrastructure layer I would have an adapter that implements the port by reading the html data from the remote portal (for example using a REST API ) and constructing the aggregates from that data.

    In the application layer I would have an application service in which you inject both the port and the player profile aggregate repository that deals with the local db. The application service calls the port to construct the aggregates, and then calls the repository to store them.

    I would run this application service periodically.

    This would be an async integration without events, but you could implement it with events if the remote portal fires events.