NDB.Platform.Core 1.0.0

dotnet add package NDB.Platform.Core --version 1.0.0
                    
NuGet\Install-Package NDB.Platform.Core -Version 1.0.0
                    
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="NDB.Platform.Core" Version="1.0.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="NDB.Platform.Core" Version="1.0.0" />
                    
Directory.Packages.props
<PackageReference Include="NDB.Platform.Core" />
                    
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 NDB.Platform.Core --version 1.0.0
                    
#r "nuget: NDB.Platform.Core, 1.0.0"
                    
#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 NDB.Platform.Core@1.0.0
                    
#: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=NDB.Platform.Core&version=1.0.0
                    
Install as a Cake Addin
#tool nuget:?package=NDB.Platform.Core&version=1.0.0
                    
Install as a Cake Tool

NDB.Platform.Core

<div align="center">

The foundation library for every NDB Platform project

Built by PT. Navigate Digital Boundaries โ€” Navigate Digital Boundaries

NuGet NuGet Downloads License: GPL v3 .NET

</div>


What is NDB.Platform.Core?

NDB.Platform.Core is the shared foundation used across every NDB Platform project. It provides the building blocks that every enterprise application needs โ€” result patterns, CQRS infrastructure, utility helpers, HTTP client infrastructure, and a set of clean contracts for storage, permissions, exports, notifications, and messaging.

Goal: Cut 60โ€“80% of boilerplate setup time on every new NDB project by shipping verified, production-ready building blocks.


Installation

dotnet add package NDB.Platform.Core

<PackageReference Include="NDB.Platform.Core" Version="1.0.0" />

Table of Contents

Namespace Documentation Description
NDB.Platform.Abstraction ๐Ÿ“„ Abstraction Result pattern, shared contracts (storage, permissions, exports, notification, messaging)
NDB.Platform.Cqrs ๐Ÿ“„ CQRS Compile-time CQRS via Mediator.SourceGenerator, logging & validation pipeline
NDB.Platform.Kit ๐Ÿ“„ Kit Utility helpers: caching, crypto, format, guards, identifiers, mapping, parse, text
NDB.Platform.Http ๐Ÿ“„ Http Typed HTTP client infrastructure: resilience, token refresh, PII scrubbing
โ€” ๐Ÿ”ฌ Analyzers Roslyn analyzer NDB001 โ€” compile-time enforcement of Result factory pattern
โ€” ๐Ÿงช Tests Unit test suite โ€” 33 test files, net8.0 + net10.0, coverage โ‰ฅ80%

Minimum Setup

// Program.cs
using NDB.Platform.Cqrs;
using NDB.Platform.Kit.Mapping;

// Register CQRS pipeline (logging + validation behaviors)
builder.Services.AddNdbCqrs(typeof(Program).Assembly);

// Register Mediator source-generated dispatcher
// Must be called AFTER AddNdbCqrs
builder.Services.AddMediator(opt => opt.ServiceLifetime = ServiceLifetime.Scoped);

// Register Mapster auto-mapping profiles
builder.Services.AddNdbMapping(typeof(Program).Assembly);

Core Concepts

1 ยท Result Pattern (No Exceptions, No Nulls)

Every operation returns a Result or Result<T>. No throwing exceptions for expected failures, no returning null.

// Factory-only โ€” constructor is private, enforced by Roslyn analyzer NDB001
return Result.Success(dto);
return Result.NotFound("User not found");
return Result.BadRequest("Email already registered");
return Result.Forbidden("Access denied");
return Result.Conflict("Duplicate entry");
return Result.ValidationError(errors);   // IDictionary<string, string[]>
return Result.Error("Unexpected error", exception);

// Reading the result
var result = await handler.Handle(query, ct);
if (!result.IsSuccess) return result.ToActionResult();
var value = result.Value; // guaranteed non-null when IsSuccess

Types available:

Type Use case
Result Void operations (Delete, Publish, Send)
Result<T> Operations returning a value
ListResult<T> Flat collection
PagedResult<T> Paginated collection with metadata
CollectionResult<T> Generic collection

The Roslyn analyzer NDB001 triggers a compile-time error if new Result<T>() is used directly. Always use the factory methods.


2 ยท CQRS (Compile-time, Zero Reflection)

Commands and queries are dispatched at compile time by Mediator.SourceGenerator. No runtime reflection, no performance overhead.

// Query
public sealed record GetOrderQuery(Guid Id) : IQuery<Result<OrderDto>>;

public sealed class GetOrderHandler(AppDbContext db)
    : IQueryHandler<GetOrderQuery, Result<OrderDto>>
{
    public async ValueTask<Result<OrderDto>> Handle(GetOrderQuery q, CancellationToken ct)
    {
        var order = await db.Orders.FindAsync([q.Id], ct);
        return order is null
            ? Result.NotFound($"Order {q.Id} not found")
            : Result.Success(order.Adapt<OrderDto>());
    }
}

// Command
public sealed record CancelOrderCommand(Guid Id, string Reason) : ICommand<Result>;

public sealed class CancelOrderHandler(AppDbContext db)
    : ICommandHandler<CancelOrderCommand, Result>
{
    public async ValueTask<Result> Handle(CancelOrderCommand cmd, CancellationToken ct)
    {
        var updated = await db.Orders
            .Where(o => o.Id == cmd.Id && o.Status != "CANCELLED")
            .ExecuteUpdateAsync(s => s
                .SetProperty(o => o.Status, "CANCELLED")
                .SetProperty(o => o.CancelReason, cmd.Reason), ct);

        return updated == 0 ? Result.NotFound("Order not found") : Result.Success();
    }
}

Automatic pipeline behaviors (registered by AddNdbCqrs):

Behavior What it does
LoggingBehavior Logs request type, execution duration, and status
ValidationBehavior Runs all IValidator<TRequest> before the handler; returns ValidationError on failure

3 ยท Infrastructure Contracts

All interfaces in Abstraction.* are pure contracts โ€” no implementations shipped in this package. Register your implementation in the consuming project's DI container.

IFileStore โ€” Provider-agnostic file storage:

// Inject without knowing whether it's Local, S3, or Azure:
public class UploadHandler(IFileStore store) : ICommandHandler<UploadCommand, Result<string>>
{
    public async ValueTask<Result<string>> Handle(UploadCommand cmd, CancellationToken ct)
    {
        var key = await store.SaveAsync(cmd.Stream, cmd.FileName, cmd.MimeType, ct);
        return Result.Success(key);
    }
}

// Register in Program.cs:
builder.Services.AddSingleton<IFileStore, LocalFileStore>();
// or: builder.Services.AddSingleton<IFileStore, S3FileStore>();

IPermissionResolver โ€” Granular RBAC:

// Effective permissions = union(role_permissions) + grants(ALLOW) - grants(DENY)
// DENY always takes precedence
builder.Services.AddScoped<IPermissionResolver, PermissionResolver>();

INotificationDispatcher โ€” Multi-channel fan-out:

await dispatcher.DispatchAsync(
    recipientId: userId,
    category: "WORKFLOW",
    title: "Task assigned to you",
    body: $"Task '{task.Title}' requires your attention",
    actionUrl: $"/tasks/{task.Id}",
    priority: "HIGH");

IExportRenderer โ€” Format-agnostic export:

// Render to CSV or XLSX depending on implementation:
var bytes = await renderer.RenderAsync(dataset, ct);
return File(bytes, renderer.MimeType, $"export.{renderer.Extension}");

4 ยท Caching (Memory + Distributed)

using NDB.Platform.Kit.Caching;

// IMemoryCache โ€” cache-aside with TTL presets:
var roles = await cache.GetOrSetAsync(
    CacheKeyBuilder.For("roles", "all"),
    () => db.Roles.OrderBy(r => r.Name).ToListAsync(ct),
    CacheEntryDefaults.DefaultMasterDataOptions());  // 60s sliding / 1h absolute

// IDistributedCache (Redis) โ€” same pattern:
var settings = await distributedCache.GetOrSetAsync(
    CacheKeyBuilder.For("settings", orgId),
    () => db.Settings.Where(s => s.OrgId == orgId).ToListAsync(ct),
    DistributedCacheExtensions.MasterDataOptions(),  // 15 min absolute
    ct);

// Cache key builder โ€” consistent format, no magic strings:
CacheKeyBuilder.For("user", "perms", userId)  // โ†’ "user:perms:{guid}"

5 ยท Crypto

using NDB.Platform.Kit.Crypto;

// BCrypt password hashing:
var hash = PasswordHasher.Hash("plaintextPassword");
bool ok   = PasswordHasher.Verify("plaintextPassword", hash);

// Blake3 one-way hashing for PII indexing (NIK, email):
var nik = PiiHasher.Hash("3271234567890001");

// Base64 encoding:
string encoded = Base64Helper.Encode(bytes);
string urlSafe = Base64Helper.EncodeUrlSafe(bytes);
byte[] decoded = Base64Helper.Decode(encoded);

6 ยท Identifiers

using NDB.Platform.Kit.Identifiers;

var ulid      = IdGenerator.NewUlid();       // sortable, URL-safe
var nanoid    = IdGenerator.NewNanoid();     // short, URL-safe
var nanoid8   = IdGenerator.NewNanoid(8);   // custom length
var snowflake = IdGenerator.NextSnowflake(); // monotonic 64-bit

var correlationId = CorrelationId.Generate(); // "req-{ulid}"

7 ยท Object Mapping (Mapster)

using NDB.Platform.Kit.Mapping;

// Auto-map from entity to DTO:
public class OrderDto : IMapFrom<Order>
{
    public Guid Id { get; set; }
    public string Status { get; set; } = default!;
    // Fields with matching names are mapped automatically
}

// Use in handler:
var dto  = order.Adapt<OrderDto>();
var list = orders.Adapt<List<OrderDto>>();

// Efficient EF projection (no over-fetching):
var page = await db.Orders
    .ProjectToType<OrderDto>()
    .ToPagedAsync(req.Page, req.Size, ct);

8 ยท HTTP Client (Service-to-Service)

using NDB.Platform.Http;

// Register typed client:
builder.Services.AddNdbHttpClient<IInventoryApi, InventoryApi>(opt =>
{
    opt.BaseUrl = config["Services:Inventory:BaseUrl"]!;
    opt.EnableRetry = true;
});

// Implement:
public class InventoryApi(IHttpClientHelper http) : BaseApiService(http), IInventoryApi
{
    public Task<HttpResult<StockDto>> GetStockAsync(Guid productId, CancellationToken ct)
        => GetAsync<StockDto>($"/stock/{productId}", ct);

    public Task<HttpResult<Guid>> ReserveAsync(ReserveStockRequest req, CancellationToken ct)
        => PostAsync<ReserveStockRequest, Guid>("/stock/reserve", req, ct);
}

// Check result without try/catch:
var result = await inventoryApi.GetStockAsync(productId, ct);
if (!result.IsSuccess)
    return Result.Error($"Inventory service error: {result.Error}");

var stock = result.Value;

Requirements

Requirement Detail
.NET 8.0 or 10.0
AddMediator() Must be called in the entry-point project (Mediator.SourceGenerator)
DI for contracts All Abstraction.* interfaces require a registered implementation
FluentValidation Validators are discovered automatically via AddNdbCqrs(Assembly[])

Ecosystem

Package Version Role
NDB.Platform.Core 1.0.0 Foundation โ† you are here
NDB.Platform.Data 1.0.0 EF Core audit, CodeGen, IQueryable extensions
NDB.Platform.API coming soon JWT auth, Swagger, Hangfire, middleware, permission handler

Open Source

NDB.Platform.Core is free and open source, licensed under GPL v3. You are free to use it in your own projects, fork it, clone it, and contribute back.

git clone https://github.com/ndbco/NDB.Platform.Core.git

Contributing

All contributions are welcome โ€” bug reports, feature requests, documentation improvements, and pull requests.

  1. Fork the repository on GitHub
  2. Create a branch: git checkout -b feat/my-improvement
  3. Make your changes and add tests
  4. Ensure all tests pass: dotnet test
  5. Open a pull request

Guidelines:

  • All public API must have XML doc comments in English
  • Unit test coverage must stay at or above 80%
  • No breaking changes without a major version bump
  • Follow the existing code style (.editorconfig is included)

Reporting Issues

Open an issue at github.com/ndbco/NDB.Platform.Core/issues.


<div align="center">

PT. Navigate Digital Boundaries ยท ndb.co.id

Navigate Digital Boundaries

GPL v3 ยท Copyright ยฉ PT. Navigate Digital Boundaries ยท Open Source

</div>

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 is compatible.  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 (2)

Showing the top 2 NuGet packages that depend on NDB.Platform.Core:

Package Downloads
NDB.Platform.Api

The web API layer library for NDB Platform projects. Wraps ASP.NET Core infrastructure into consistent, pre-configured building blocks: Authentication โ€” JWT Bearer dual-token (access 15 min / refresh 7 days), HMAC-SHA256, fail-fast option validation, Token-Expired header support, and configurable token refresh. Authorization โ€” Granular permission-based authorization via IPermissionResolver (RequirePermissionAttribute + dynamic policy provider), role-based shorthand (NdbRequireRoleAttribute), and superadmin bypass via JWT claim or role. Result โ†’ HTTP โ€” ResultActionFilter auto-converts Result/Result<T>/PagedResult<T>/ CollectionResult<T> return values to a consistent ApiResponse JSON envelope. Middleware โ€” CorrelationIdMiddleware (X-Correlation-ID propagation), RequestLoggingMiddleware (method/path/duration/actor/status), GlobalExceptionHandler (unhandled exceptions โ†’ 500 ApiResponse JSON). Hangfire โ€” Provider-agnostic background job wrapper. Install the storage provider in the consuming project. Dashboard protected by timing-safe Basic Auth. Swagger โ€” Swashbuckle pre-configured with JWT Bearer security scheme and API versioning. Additional โ€” CORS policy, ForwardedHeaders (reverse proxy), health check endpoints (/health/live, /health/ready), API versioning (URL segment), BaseController with lazy Mediator and current-user properties, IPdfRenderer abstraction. Requires NDB.Platform.Core and NDB.Platform.Data (installed automatically).

NDB.Platform.Data

The data layer library for NDB Platform projects. Provides three production-ready building blocks: (1) Audit Trail โ€” EF Core ChangeTracker integration that automatically records every Insert, Update, and Delete with actor identity, timestamp, and field-level diffs. Actor info is injected from JWT claims via a Mediator pipeline behavior. Storage is pluggable: implement IAuditWriter to write audit entries anywhere (SQL table, Elasticsearch, file log, etc.). (2) IQueryable Extensions โ€” a complete set of composable extensions for EF Core queries: ToPagedResultAsync, ToListResultAsync, ApplyFilter, ApplySort, ApplySorts, ApplySearch (in-memory safe), WhereContainsIgnoreCase (ILIKE on PostgreSQL / EF.Functions.Like fallback on SQL Server), IsUniqueViolation, IsForeignKeyViolation, DetachAll, and EnsureNotTracked. All extensions are provider-agnostic by design. (3) CRUD Code Generator โ€” scaffolds a complete handler + controller stack from an EF entity via Handlebars templates (10 built-in: Add/Edit/Delete handlers, GetById/GetList/GetPaged handlers, Request/Response/Contract DTOs, REST controller). Templates are embedded and fully customizable. A PowerShell script is included for one-command scaffold runs. Requires NDB.Platform.Core (installed automatically). Provider (Npgsql / SqlServer) is NOT included โ€” install the provider in the consuming project.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.0.0 130 5/31/2026