需要有一个软件设计,以支持从一个支付系统(E)逐步迁移到另一个支付系统(Stripe)。信用卡数据是存储在外部,所以我想使用这两个系统并行一段时间,不让所有用户重新输入信用卡数据。
下面是我目前在领域核心中拥有的内容:
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向正确的适配器发出调用,并应该注入消费代码:
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的适配器:
[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适配器程序集中的内容:
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:
public interface IEXactAdapter : IPaymentAdapter
{
}你对这种设计方法有什么看法?看上去够干净吗?
发布于 2019-12-19 09:51:50
您的设计可以大大简化,消除泛型,反射和多种变化的支付界面。下面是我应该如何做到的(我跳过了异步/等待以保持示例的简单性):
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
发布于 2019-12-18 23:08:23
我认为在不同的名称下拥有相同的接口忽略了接口的意义。接口的要点是,您可以让provider1、provider2、unittestprovider、上传到my等都公开相同的接口。
表示提供程序模型
一旦您有了多个提供程序,它们通常会使用依赖注入框架进行映射。换句话说,你只说一次
RegisterType(IPaymentService, PaymentService1)每当需要支付服务时,有人会说
var provider = GetInstance(IPaymentService)而该框架将获得上述提供商。这减少了作出这种改变的影响。
更改通知的设计
我对您的目标的理解是,您希望在处理交易时获取信用卡信息,以便它们可以与您的新系统一起存储。
要实现这一点,请创建包装实际支付提供者的包装器。包装器还将公开IPaymentService,使用内部提供程序执行实际工作,并执行自己的任务。
信用卡更改最好用发行者/订阅者模型表示。在这个模型中,发布者创建一个消息,表明它已经获得了数据的知识。它发布此消息时不知道任何收件人。收件人可以订阅这类消息,并按他们认为合适的方式处理它们。这两个优点是收件人没有硬编码,因此易于更改,而且实现通常是异步的,因此不会阻碍您的付款处理。
https://softwareengineering.stackexchange.com/questions/402671
复制相似问题