Skip to content
Bitzsoft.Integrationsbitzsoft.integrations

Concept

三层包模式

抽象层、厂商实现层和聚合层的职责边界与代码组织约定。

Last updated

三层包模式是 Bitzsoft.Integrations 最核心的架构约定。它把”领域契约”和”厂商实现”严格分离,让消费者按需安装、按需注册,切换厂商只改配置不改代码。

以 Payment 域为例:

Bitzsoft.Integrations.Payment ← 抽象层
├── Bitzsoft.Integrations.Payment.Alipay ← 实现层:支付宝
├── Bitzsoft.Integrations.Payment.WeChatPay ← 实现层:微信支付
├── Bitzsoft.Integrations.Payment.Stripe ← 实现层:Stripe
└── Bitzsoft.Integrations.Payment.All ← 聚合层

第一层:抽象包

包名Bitzsoft.Integrations.{域}(如 Bitzsoft.Integrations.Payment

定义领域契约,不含任何厂商逻辑。包含五类成员:

领域接口

src/Bitzsoft.Integrations.Payment/Interfaces/IPaymentProvider.cs
public interface IPaymentProvider
{
string Code { get; }
Task<PaymentResult<PaymentOrderResult>> CreateOrderAsync(PaymentOrderRequest request, CancellationToken ct = default);
Task<PaymentResult<PaymentStatusResult>> QueryStatusAsync(string outTradeNo, CancellationToken ct = default);
Task<PaymentResult> CloseOrderAsync(string outTradeNo, CancellationToken ct = default);
Task<PaymentResult<RefundResult>> RefundAsync(RefundRequest request, CancellationToken ct = default);
Task<PaymentResult<CallbackVerificationResult>> VerifyCallbackAsync(CallbackPayload payload, CancellationToken ct = default);
}

所有厂商实现这一个接口。消费者注入 IPaymentProvider,不用关心后面是哪家厂商。

共享 DTO / 模型 / 枚举

跨厂商统一的数据结构。如 PaymentOrderRequest(下单请求)、PaymentStatusResult(状态查询结果)、PaymentScene 枚举(App / Web / H5 / Code)。

结果包装

src/Bitzsoft.Integrations.Payment/PaymentResult.cs
public sealed class PaymentResult<T>
{
public bool IsSuccess { get; init; }
public T? Data { get; init; }
public string? ErrorCode { get; init; }
public string? ErrorMessage { get; init; }
public static PaymentResult<T> Success(T data) => new() { IsSuccess = true, Data = data };
public static PaymentResult<T> Fail(string errorCode, string errorMessage) => new() { IsSuccess = false, ErrorCode = errorCode, ErrorMessage = errorMessage };
}

领域异常

public class PaymentException : Exception
{
public string ProviderName { get; }
public string? ErrorCode { get; }
public string? ProviderMessage { get; }
// 消息格式: "[{providerName}] {errorCode}: {providerMessage}"
}

供应商标识常量

src/Bitzsoft.Integrations.Payment/Models/PaymentProviderCode.cs
public static class PaymentProviderCode
{
public const string Alipay = "Alipay";
public const string WeChatPay = "WeChatPay";
public const string Stripe = "Stripe";
}

抽象包通过 InternalsVisibleTo 向自己的厂商包和聚合包开放 internal 成员:

<!-- src/Bitzsoft.Integrations.Payment/Bitzsoft.Integrations.Payment.csproj -->
<ItemGroup>
<InternalsVisibleTo Include="Bitzsoft.Integrations.Payment.Alipay" />
<InternalsVisibleTo Include="Bitzsoft.Integrations.Payment.WeChatPay" />
<InternalsVisibleTo Include="Bitzsoft.Integrations.Payment.Stripe" />
<InternalsVisibleTo Include="Bitzsoft.Integrations.Payment.All" />
</ItemGroup>

第二层:厂商实现包

包名Bitzsoft.Integrations.{域}.{厂商}(如 Bitzsoft.Integrations.Payment.Alipay

每家厂商一个独立包。包含:

Bitzsoft.Integrations.Payment.Alipay/
├── AlipayOptions.cs ← 厂商 Options(public sealed)
├── AlipayPaymentProvider.cs ← Provider 实现(public sealed,构造函数 internal)
├── AlipayProviderDescriptor.cs ← Provider 描述(internal static Instance)
├── ServiceCollectionExtensions.cs ← DI 扩展(public static AddBitzsoftAlipayPayment)
├── GlobalUsings.cs
└── Internal/
├── AlipayHttpClient.cs ← HTTP 封装(internal sealed)
└── AlipaySigner.cs ← RSA 签名器(internal sealed, IDisposable)

厂商 Options

src/Bitzsoft.Integrations.Payment.Alipay/AlipayOptions.cs
public sealed class AlipayOptions
{
public string AppId { get; set; } = string.Empty;
public string AppPrivateKey { get; set; } = string.Empty;
public string AlipayPublicKey { get; set; } = string.Empty;
public string Gateway { get; set; } = "https://openapi.alipay.com/gateway.do";
public string NotifyUrl { get; set; } = string.Empty;
public string SignType { get; set; } = "RSA2";
public TimeSpan Timeout { get; set; } = TimeSpan.FromSeconds(30);
// ① HttpClientName 默认 nameof(Provider类名),命名 HttpClient 的关键约定
public string HttpClientName { get; set; } = nameof(AlipayPaymentProvider);
}

字符串字段统一以 = string.Empty 兜底,避免 null。

Provider 实现

public sealed 类,构造函数 internal(禁止手动 new,只走 DI)。每个方法按 catch (DomainException)catch (HttpRequestException) 分类型处理,转成 PaymentResult

DI 扩展方法

src/Bitzsoft.Integrations.Payment.Alipay/ServiceCollectionExtensions.cs
namespace Microsoft.Extensions.DependencyInjection;
public static class AlipayServiceCollectionExtensions
{
public static IServiceCollection AddBitzsoftAlipayPayment(
this IServiceCollection services, Action<AlipayOptions> configure) { ... }
public static IServiceCollection AddBitzsoftAlipayPayment(
this IServiceCollection services, IConfiguration configuration,
string sectionKey = "Alipay") { ... }
}

命名约定 AddBitzsoft{厂商}{域}(),扩展类放 Microsoft.Extensions.DependencyInjection 命名空间。

第三层:聚合包

包名Bitzsoft.Integrations.{域}.All(如 Bitzsoft.Integrations.Payment.All

自身几乎不产 IL(IncludeBuildOutput=false),只靠 ProjectReference 把所有厂商实现拉进来。唯一编译产物是一个聚合 DI 扩展方法:

src/Bitzsoft.Integrations.Payment.All/ServiceCollectionExtensions.cs
public static class PaymentServiceCollectionExtensions
{
public static IServiceCollection AddBitzsoftPaymentAll(
this IServiceCollection services, IConfiguration configuration,
string sectionKey = "Payment")
{
var section = configuration.GetSection(sectionKey);
// ① 按配置节存在性自动注册——不存在的厂商不会被注册
if (section.GetSection("Alipay").Exists())
services.AddBitzsoftAlipayPayment(configuration, $"{sectionKey}:Alipay");
if (section.GetSection("WeChatPay").Exists())
services.AddBitzsoftWeChatPayPayment(configuration, $"{sectionKey}:WeChatPay");
if (section.GetSection("Stripe").Exists())
services.AddBitzsoftStripePayment(configuration, $"{sectionKey}:Stripe");
return services;
}
}

消费者只装一个 .All 包、写一段配置、调一个方法,就能按配置节的存在性自动注册对应厂商。

聚合包的 csproj 有两个特殊属性:

<IncludeBuildOutput>false</IncludeBuildOutput>
<IncludeSymbols>false</IncludeSymbols>

并用 build/NuGet/_._ 占位文件放进各 TFM 的 lib 目录,避免 NuGet “空 lib” 警告。

相关

100%

滚轮或按钮缩放 · 放大后拖动画面 · 双击切换 100% / 200%