TCIS.Pluggable.Engine 1.0.0-rc.12

This is a prerelease version of TCIS.Pluggable.Engine.
There is a newer prerelease version of this package available.
See the version list below for details.
dotnet add package TCIS.Pluggable.Engine --version 1.0.0-rc.12
                    
NuGet\Install-Package TCIS.Pluggable.Engine -Version 1.0.0-rc.12
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="TCIS.Pluggable.Engine" Version="1.0.0-rc.12" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="TCIS.Pluggable.Engine" Version="1.0.0-rc.12" />
                    
Directory.Packages.props
<PackageReference Include="TCIS.Pluggable.Engine" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add TCIS.Pluggable.Engine --version 1.0.0-rc.12
                    
#r "nuget: TCIS.Pluggable.Engine, 1.0.0-rc.12"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package TCIS.Pluggable.Engine@1.0.0-rc.12
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=TCIS.Pluggable.Engine&version=1.0.0-rc.12&prerelease
                    
Install as a Cake Addin
#tool nuget:?package=TCIS.Pluggable.Engine&version=1.0.0-rc.12&prerelease
                    
Install as a Cake Tool

TCIS.Pluggable.Engine

The core engine library for the TCIS Pluggable Pipeline architecture. It provides a unified discovery mechanism (Modules + Steps + Rules + Services), Pipeline Analyzer, and Pipeline Engine.

📦 Installation

dotnet add package TCIS.Pluggable.Engine

🚀 Registration / Setup (Program.cs)

Register the Pluggable Engine and Discovery:

using TCIS.Pluggable.Engine.Modules;

var builder = WebApplication.CreateBuilder(args);

// Platform: nghiệp vụ chuẩn, dùng chung mọi Site.
builder.Services.AddPluggablePipeline(
    builder.Configuration,
    typeof(GatePlatformModule).Assembly
);

// Plugin đặc thù theo Site — tham chiếu TƯỜNG MINH, không quét thư mục.
builder.Services.AddSitePlugins(
    builder.Configuration,
    typeof(CatLaiModule).Assembly
);

Plugin được nạp bằng tham chiếu tĩnh, không phải nạp động

Mỗi bản triển khai có Host riêng, và các Host khác nhau đúng ở danh sách assembly truyền vào AddSitePlugins:

// Cảng lớn triển khai riêng — build từ Platform + plugin của cảng đó
.AddSitePlugins(configuration, typeof(CatLaiModule).Assembly);

// Nhiều cảng nhỏ dùng chung một bản triển khai
.AddSitePlugins(configuration,
    typeof(SiteAModule).Assembly,
    typeof(SiteBModule).Assembly);

Vì sao không quét thư mục DLL. Tập plugin của mỗi bản triển khai đã biết tại thời điểm build, nên AssemblyLoadContext không mua được gì — trong khi tham chiếu tĩnh mang lại thứ quan trọng hơn nhiều: trình biên dịch kiểm tra plugin có còn khớp Core hay không.

Với nạp động, Core đổi chữ ký một interface thì plugin cũ vẫn build được (nó build riêng), và lỗi chỉ nổ lúc chạy, trên production, của riêng cảng đó. Với tham chiếu tĩnh, build đỏ ngay và chỉ đích danh plugin nào gãy. Đó là hàng rào rẻ nhất chống trôi dạt giữa Core và Plugin — xem md/28.

Plugin bắt buộc khai báo SiteCode khác DEFAULT. Quên đặt là lỗi âm thầm nguy hiểm nhất của mô hình này: module sẽ được nạp cho mọi Site và ghi đè nghiệp vụ chuẩn của cả tập đoàn. AddSitePlugins kiểm mọi module trong assembly và ném PipelineDiscoveryException lúc khởi động.

⚙️ Configuration

Cấu hình hậu tố cho việc tự động đăng ký DI trong appsettings.json:

{
  "TLogSettings": {
    "AopSettings": {
      "DiRegistrationSuffixes": [
        "Service",
        "Provider",
        "Mapper",
        "Factory",
        "Manager",
        "Repository",
        "Policy"
      ]
    }
  }
}

Resilience — Engine KHÔNG áp policy lên Step

Không có timeout, retry hay circuit breaker ở tầng Step. Đây là quyết định có chủ đích, không phải thiếu sót — xem md/28.

Chính sách chịu lỗi thuộc về nơi lời gọi vượt qua ranh giới hạ tầng, không thuộc về logic nghiệp vụ chạy in-process:

Nhu cầu Đặt ở đâu
Giới hạn thời gian truy vấn DB AddPluggableSqlServer<T>(commandTimeoutSeconds: 30)thứ duy nhất thật sự huỷ được query ở phía server
Gọi API / gRPC ngoài TCIS.Http đã có policy Polly riêng; gRPC dùng deadline
Chốt chặn cuối cho toàn request AddRequestTimeouts của ASP.NET Core
Phát hiện Step nào của cảng nào đang hỏng Số đo tcis.pipeline.step.* — xem mục dưới

Vì sao timeout ở tầng Step không có tác dụng như người ta tưởng. Polly dùng cooperative cancellation: nó huỷ token rồi ném ngoại lệ cho caller, nhưng Step vẫn chạy tiếp — .NET không ép dừng được một task. Kết quả có thể là: client đã nhận lỗi, pipeline đã abort, rồi Step vẫn ghi xuống database vài giây sau. Timeout chặn được thật chỉ nằm ở CommandTimeout và ở tầng kết nối.

Cấu hình chốt chặn cuối:

builder.Services.AddRequestTimeouts(o =>
    o.DefaultPolicy = new RequestTimeoutPolicy { Timeout = TimeSpan.FromSeconds(60) });

app.UseTCISCore();       // TExceptionMiddleware phải BỌC NGOÀI để phân loại được sự cố
app.UseRequestTimeouts();

TExceptionMiddleware phân biệt server hết hạn với client tự ngắt bằng IHttpRequestTimeoutFeature.RequestTimeoutToken — cả hai đều biểu hiện bằng OperationCanceledException nên không thể phân biệt bằng kiểu ngoại lệ.

Tình huống HTTP Mức log
Request vượt ngưỡng thời gian của server 504 GATEWAY_TIMEOUT Error → cảnh báo kích hoạt
Client đóng kết nối 499 CLIENT_CLOSED Information → không cảnh báo

Ranh giới này được ghim bằng RequestTimeoutClassificationTests. Phân loại nhầm không chỉ sai mã HTTP mà còn sai mức log, khiến sự cố quá tải trôi qua mọi hệ thống giám sát.

📈 Số đo — cơ chế phát hiện sự cố duy nhất

Circuit breaker từng đảm nhiệm việc phát hiện một Step hỏng lặp lại. Nó đã bị gỡ bỏ vì bảo vệ sai mối đe doạ, nhưng tín hiệu thì vẫn cần. Từ nay cảnh báo là cơ chế cắt mạch duy nhất.

builder.Services.AddOpenTelemetry()
    .WithMetrics(m => m.AddMeter(PipelineMetrics.MeterName));   // "TCIS.Platform.Pipelines"
Số đo Ý nghĩa
tcis.pipeline.step.duration Thời gian thực thi Step (ms)
tcis.pipeline.step.errors Step ném ngoại lệ — sự cố hạ tầng
tcis.pipeline.step.business_failures Step dừng pipeline bằng context.Failnghiệp vụ

Cả ba đều mang nhãn tcis.site_code, tcis.step_key, tcis.context.

Lỗi nghiệp vụ được đếm riêng. Từ chối một container quá tải trọng là hệ thống đang làm đúng việc của nó — trộn hai loại vào một số đo sẽ khiến mọi ngưỡng cảnh báo trở nên vô nghĩa.

Và nhãn tcis.site_code là bắt buộc chứ không phải trang trí: nhiều cảng nhỏ dùng chung một tiến trình, nên tổng số lỗi toàn hệ thống che mất đúng tình huống cần cảnh báo nhất — một cảng hỏng hoàn toàn trong khi chín cảng còn lại vẫn xanh.

Quy tắc cảnh báo gợi ý (PromQL):

# Một Step của một cảng hỏng liên tục — thay cho vai trò của circuit breaker
sum by (tcis_site_code, tcis_step_key) (rate(tcis_pipeline_step_errors_total[5m])) > 0.2

# Một Step chậm bất thường — dấu hiệu sớm của DB treo
histogram_quantile(0.95,
  sum by (le, tcis_site_code, tcis_step_key) (rate(tcis_pipeline_step_duration_bucket[5m]))) > 5000

Hành vi lỗi của Engine

Tình huống Hành vi
Step gọi context.Fail(...) Lỗi nghiệp vụ — ở lại trong Context, pipeline dừng (hoặc tiếp tục nếu ContinueOnError).
Step ném Exception Lỗi hạ tầng — Engine ghi log rồi ném tiếp, giữ nguyên kiểu để TExceptionMiddleware map đúng HTTP status (503). Engine không hạ cấp thành lỗi nghiệp vụ. Các Step sau không chạy.
Không resolve được Step từ DI PipelineDiscoveryException. Engine không fallback sang ActivatorUtilities — làm vậy sẽ mất toàn bộ AOP Logging/Telemetry mà không ai biết.

Chỉ còn ba tình huống, và không tình huống nào bỏ qua một Step. Mỗi Step hoặc chạy, hoặc làm dừng pipeline — nên lớp lỗi "pipeline báo thành công dù Step nghiệp vụ chưa chạy" (phát hiện P1-8) nay không còn tồn tại về mặt cấu trúc.

💡 Usage

Automatically register services by suffix (Auto-MultiTenantService) or utilize Attributes:

using TCIS.Pluggable.Abstractions.Pipelines.Attributes;
using TCIS.Pluggable.Abstractions.Pipelines.Abstractions;

[PipelineStep(StepKey = "ValidateOrder", Priority = 1, ExecutionOrder = 10)]
public class ValidateOrderStep : IPipelineStep<OrderContext>
{
    public Task ExecuteAsync(OrderContext context, CancellationToken cancellationToken)
    {
        // ... logic
        return Task.CompletedTask;
    }
}
Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (1)

Showing the top 1 NuGet packages that depend on TCIS.Pluggable.Engine:

Package Downloads
TCIS.Pluggable.Engine.Autofac

TCIS Core Framework is an application framework for building modular, multi-tenant applications on ASP.NET Core. Autofac integration for TCIS.Pluggable.Engine

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.0.0-rc.33 20 8/21/2026
1.0.0-rc.32 24 8/21/2026
1.0.0-rc.31 31 8/21/2026
1.0.0-rc.30 28 8/21/2026
1.0.0-rc.29 25 8/21/2026
1.0.0-rc.28 29 8/21/2026
1.0.0-rc.27 28 8/21/2026
1.0.0-rc.26 41 8/20/2026
1.0.0-rc.25 41 8/20/2026
1.0.0-rc.24 43 8/20/2026
1.0.0-rc.23 39 8/20/2026
1.0.0-rc.22 45 8/19/2026
1.0.0-rc.21 45 8/19/2026
1.0.0-rc.20 59 8/18/2026
1.0.0-rc.19 72 8/13/2026
1.0.0-rc.18 57 8/13/2026
1.0.0-rc.17 54 8/13/2026
1.0.0-rc.16 62 8/13/2026
1.0.0-rc.15 60 8/12/2026
1.0.0-rc.12 58 8/10/2026
Loading failed