需要有一个软件设计,以支持从一个支付系统(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.Charge(id.Value, amount);
}请注意,E-xact适配器(退役适配器)应该基于IPaymentAdaptor,而不是IMainPaymentAdapter:
public interface IEXactAdapter : IPaymentAdapter
{
}你对这种设计方法有什么看法?看上去够干净吗?
发布于 2019-12-19 19:54:19
这是一个简化的版本,感谢这个回答。
public interface IMainPaymentProvider : IPaymentProvider
{
}
public interface IPaymentProvider : IPaymentService
{
string Name { get; }
}
public interface IPaymentGateway : IPaymentService
{
}
public interface IPaymentService
{
Task<BillingId> RegisterAsync(CreditCard card);
Task ChargeAsync(BillingId id, decimal amount);
}其中:
[TypeConverter(typeof(BillingIdTypeConverter))]
[JsonConverter(typeof(BillingIdJsonConverter))]
public class BillingId
{
public static BillingId Parse(string text) =>
IsNullOrWhiteSpace(text) ? null :
text.Split(new[] { ':' }, StringSplitOptions.RemoveEmptyEntries) is string[] p && p.Length == 2
? new BillingId(p[0], p[1])
: throw new FormatException();
public BillingId(string provider, string value) =>
(Provider, Value) = (provider, value);
public string Provider { get; }
public string Value { get; }
public override string ToString() =>
$"{Provider}:{Value}";
}因此,支付网关看起来像是:
[Service]
public class PaymentGateway : IPaymentGateway
{
public PaymentGateway(IMainPaymentProvider mainProvider, params IPaymentProvider[] providers) =>
(MainProvider, Providers) =
(mainProvider, providers.ToDictionary(p => p.Name));
IPaymentProvider MainProvider { get; }
IReadOnlyDictionary<string, IPaymentProvider> Providers { get; }
public async Task<BillingId> RegisterAsync(CreditCard card) =>
await MainProvider.RegisterAsync(card);
public async Task ChargeAsync(BillingId id, decimal amount) =>
await Providers[id.Provider].ChargeAsync(id, amount);
}一个示例提供者可以是:
public interface IStripeProvider : IMainPaymentProvider
{
}
[Service]
public class StripeProvider : IStripeProvider
{
StripeClient Client { get; } = new StripeClient();
public string Name => "Stripe";
public async Task<BillingId> RegisterAsync(CreditCard card) =>
new BillingId(Name, await Client.RegisterAsync(card));
public async Task ChargeAsync(BillingId id, decimal amount) =>
await Client.Charge(id.Value, amount);
}https://codereview.stackexchange.com/questions/234271
复制相似问题