首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >支付系统迁移

支付系统迁移
EN

Software Engineering用户
提问于 2019-12-18 19:11:05
回答 2查看 170关注 0票数 1

GitHub

需要有一个软件设计,以支持从一个支付系统(E)逐步迁移到另一个支付系统(Stripe)。信用卡数据是存储在外部,所以我想使用这两个系统并行一段时间,不让所有用户重新输入信用卡数据。

下面是我目前在领域核心中拥有的内容:

代码语言:javascript
复制
public interface IMainPaymentAdapter : IPaymentAdapter
{
}

public interface IPaymentAdapter : IPaymentService
{
}

public interface IPaymentGateway : IPaymentService
{
}

public interface IPaymentService
{
    Task<BillingId> RegisterAsync(CreditCard card);
    Task ChargeAsync(BillingId id, decimal amount);
}

其思想是IPaymentService定义API形状,IPaymentAdaptor即将为每个正在使用的支付系统实现,IPaymentGateway基于BillingId向正确的适配器发出调用,并应该注入消费代码:

代码语言:javascript
复制
public abstract class BillingId
{
    public static BillingId Parse(string text)
    {
        var parts = text.Split('/');
        return (BillingId)Activator.CreateInstance(
            typeof(BillingId<>).MakeGenericType(Type.GetType(parts[0])),
            parts[1]);
    }

    protected BillingId(string value) => Value = value;
    public string Value { get; }
    public abstract Type Adapter { get; }
    public override string ToString() => 
        $"{Adapter.AssemblyQualifiedName}/{Value}";
}

public class BillingId<TAdapter> : BillingId 
    where TAdapter : IPaymentAdapter
{
    public BillingId(string value) : base(value) { }
    public override Type Adapter => typeof(TAdapter);
}

我的网关看起来是这样的--请注意,卡片注册呼叫总是被分派到当前的IMainPaymentAdaptor实现,而其他所有东西都被发送到最初提供计费id的适配器:

代码语言:javascript
复制
[Service]
public class PaymentGateway : IPaymentGateway
{
    public PaymentGateway(IServiceProvider provider) => Provider = provider;
    IServiceProvider Provider { get; }

    public async Task<BillingId> RegisterAsync(CreditCard card) =>
        await Adapter().RegisterAsync(card);

    public async Task ChargeAsync(BillingId id, decimal amount) =>
        await Adapter(id).ChargeAsync(id, amount); 

    IPaymentAdapter Adapter() => Adapter(typeof(IMainPaymentAdapter));
    IPaymentAdapter Adapter(BillingId id) => Adapter(id.Adapter);
    IPaymentAdapter Adapter(Type type) => 
        (IPaymentAdapter)Provider.GetService(type);
}

下面是Stripe适配器程序集中的内容:

代码语言:javascript
复制
public interface IStripeAdapter : IMainPaymentAdapter
{
}

[Service]
public class StripeAdapter : IStripeAdapter
{
    StripeClient Client { get; } = new StripeClient();

    public async Task<BillingId> RegisterAsync(CreditCard card) =>
        new BillingId<IStripeAdapter>(await Client.RegisterAsync(card));

    public async Task ChargeAsync(BillingId id, decimal amount) =>
        await Client.ChargeAsync(id.Value, amount);
}

请注意,E-xact适配器(退役适配器)应该基于IPaymentAdaptor,而不是IMainPaymentAdapter

代码语言:javascript
复制
public interface IEXactAdapter : IPaymentAdapter
{
}

你对这种设计方法有什么看法?看上去够干净吗?

EN

回答 2

Software Engineering用户

回答已采纳

发布于 2019-12-19 09:51:50

您的设计可以大大简化,消除泛型,反射和多种变化的支付界面。下面是我应该如何做到的(我跳过了异步/等待以保持示例的简单性):

代码语言:javascript
复制
class BillingId {
    ProviderId providerId; // "Exact" or "Stripe" (can be a string or enum)
    RegistrationId registrationId; // an ID specific to the provider
}

interface IPaymentProvider {
    RegistrationId Register(CreditCard card);
    void Charge(RegistrationId id, decimal amount);
}

class PaymentGateway {
    Dictionary<ProviderId, IPaymentProvider> providers;
    ProviderId primaryProvider; // "Stripe"

    BillingId Register(CreditCard card) {
        var registrationId = providers[primaryProvider].Register(card);
        return new BillingId(primaryProvider, registrationId);
    }

    void Charge(BillingId billingId, decimal amount) {
        providers[billingId.ProviderId].Charge(billingId.RegistrationId, amount);
    }
}

通常,您不希望太依赖于语言的类型系统来建模域概念,例如支付提供者类型。进一步阅读:巫师和战士

另外,将提供者接口(上述示例中的IPaymentProvider)与客户端接口(PaymentGateway)分开也是个好主意,因为它们在不同的级别上工作。提供程序只需要处理RegistrationIds,而客户端API是多个提供者的外观。进一步阅读:分开API和SPI

票数 3
EN

Software Engineering用户

发布于 2019-12-18 23:08:23

我认为在不同的名称下拥有相同的接口忽略了接口的意义。接口的要点是,您可以让provider1、provider2、unittestprovider、上传到my等都公开相同的接口。

表示提供程序模型

一旦您有了多个提供程序,它们通常会使用依赖注入框架进行映射。换句话说,你只说一次

代码语言:javascript
复制
RegisterType(IPaymentService, PaymentService1)

每当需要支付服务时,有人会说

代码语言:javascript
复制
var provider = GetInstance(IPaymentService)

而该框架将获得上述提供商。这减少了作出这种改变的影响。

更改通知的设计

我对您的目标的理解是,您希望在处理交易时获取信用卡信息,以便它们可以与您的新系统一起存储。

要实现这一点,请创建包装实际支付提供者的包装器。包装器还将公开IPaymentService,使用内部提供程序执行实际工作,并执行自己的任务。

信用卡更改最好用发行者/订阅者模型表示。在这个模型中,发布者创建一个消息,表明它已经获得了数据的知识。它发布此消息时不知道任何收件人。收件人可以订阅这类消息,并按他们认为合适的方式处理它们。这两个优点是收件人没有硬编码,因此易于更改,而且实现通常是异步的,因此不会阻碍您的付款处理。

票数 1
EN
页面原文内容由Software Engineering提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://softwareengineering.stackexchange.com/questions/402671

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档