Skip to content

jkone27/DualSourceAdapter

Repository files navigation

DualSourceAdapter

NuGet Badge

Migration Adapter for a dual source API

Alt text

Configuration

Here an example configuration with 3 implementations of ICustomerRepository, a NewService, a LegacyService and an AdapterService, the usage of the library is internal to the latter and will be shown later.

MigrationOption is a configuration provided to configure the enabled MigrationSource [] and the Active (master) configuration, from which data is stored and read first.

var host = Host.CreateDefaultBuilder(args)
    .ConfigureServices(services => { 
        services.AddSingleton<LegacyService>();
        services.AddSingleton<NewService>();
        services.AddSingleton<ICustomerRepository, AdapterService>();

        var migrationOption = new MigrationOption {
            Active = MigrationSource.New,
            IsCompare = true,
            Sources = new [] {
                MigrationSource.New,
                MigrationSource.Legacy
            }
        };

        services.AddSingleton(migrationOption);
    })
    .Build();

Creating an AdapterService

The creation of the adapter service goes as follows, in this example only 2 flows need to be adapted differently, read and write, but eventually more could be given (e.g. read, write, auth)

this.readMigrator = new MigrationTaskBuilder<GetCustomerById, Customer?>(
            legacyService.GetCustomerByIdAsync,
            newService.GetCustomerByIdAsync,
            ReadCompare, // to compare data
            ReadResponseAdapter, // usually the identifier need to be adapter to match the old data
            ReadRequestAdapter // usually also new requests need to use a Map to retrieve the corresponding identifier for new data, based on old identifier
        );

conversely the write migrator is configured in a similar fashion

this.writeMigrator = new MigrationTaskBuilder<CreateCustomerRequest, Customer>(
            legacyService.SaveCutomerAsync,
            newService.SaveCutomerAsync,
            StoreCustomerReference,
            WriteResponseAdapter,
            WriteRequestAdapter
        );

Adapter Examples (may change)

    public Task ReadCompare(PrimaryResponse<Customer?> customer, SecondaryResponse<Customer?> customerNew)
    {
        logger.LogInformation("COMPARING CUSTOMERS...");
        return Task.CompletedTask;
    }

    public async Task StoreCustomerReference(PrimaryResponse<Customer> primary, SecondaryResponse<Customer> secondary)
    {
        await Task.Delay(TimeSpan.FromMilliseconds(400));

        var p = GetPrimary(primary);
        var s = GetSecondary(secondary);
    
        CustomerRefStorage.Add(p.Id, s.Id);
    }

    public PrimaryResponse<Customer?> ReadResponseAdapter(PrimaryResponse<Customer?> primary, SecondaryResponse<Customer?> secondary)
    {
        var p = GetPrimary(primary);
        var s = GetSecondary(secondary);

        if(s is not null && p is not null)
        {
            p.Id = s.Id;
            return Primary<Customer?>(p);
        }

        return primary;
    }

    public PrimaryResponse<Customer> WriteResponseAdapter(PrimaryResponse<Customer> primary, SecondaryResponse<Customer> secondary)
    {
        // keep the reference consistent for clients
        secondary.Item.Id = primary.Item.Id;
        return Primary(secondary.Item);
    }

    public GetCustomerById ReadRequestAdapter(GetCustomerById request, PrimaryResponse<Customer?> primary) 
    {
        Customer? p = primary.Item;
        
        if(p is not null)
        {
            // grab the reference for the identifier in the new storage, based on old
            request.Id = CustomerRefStorage[p.Id];
        }

        return request;
    }

    public CreateCustomerRequest WriteRequestAdapter(CreateCustomerRequest request, PrimaryResponse<Customer> _) 
    {
        return request; // NOP
    }

Usage

Migration options can be specified as same or different, depending on the usage. ideally it make sense to use the same source for different flows, but you might need specific settings or runtime changes for test of individuals, in which case, you still can, since the migration options are dispatched dynamically.

 public Task<Customer?> GetCustomerByIdAsync(GetCustomerById request)
    {
        return readMigrator.MigrationTaskAsync(migrationOption, request);
    }

    public Task<Customer> SaveCutomerAsync(CreateCustomerRequest request)
    {
        return writeMigrator.MigrationTaskAsync(migrationOption, request);
    }

About

adapter for a dual source to compare and migrate

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published