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
<PackageReference Include="NDB.Platform.Core" Version="1.0.0" />
<PackageVersion Include="NDB.Platform.Core" Version="1.0.0" />
<PackageReference Include="NDB.Platform.Core" />
paket add NDB.Platform.Core --version 1.0.0
#r "nuget: NDB.Platform.Core, 1.0.0"
#:package NDB.Platform.Core@1.0.0
#addin nuget:?package=NDB.Platform.Core&version=1.0.0
#tool nuget:?package=NDB.Platform.Core&version=1.0.0
NDB.Platform.Core
<div align="center">
The foundation library for every NDB Platform project
Built by PT. Navigate Digital Boundaries โ Navigate Digital Boundaries
</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.
- Fork the repository on GitHub
- Create a branch:
git checkout -b feat/my-improvement - Make your changes and add tests
- Ensure all tests pass:
dotnet test - 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 (
.editorconfigis 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 | Versions 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. |
-
net10.0
- BCrypt.Net-Next (>= 4.0.3)
- Blake3 (>= 2.2.1)
- DocumentFormat.OpenXml (>= 3.2.0)
- FluentValidation (>= 11.10.0)
- FluentValidation.DependencyInjectionExtensions (>= 11.10.0)
- IdGen (>= 3.0.7)
- Mapster (>= 10.0.7)
- Mapster.DependencyInjection (>= 1.0.1)
- Mediator.Abstractions (>= 2.1.7)
- Microsoft.Extensions.Caching.Abstractions (>= 8.0.0)
- Microsoft.Extensions.Caching.Memory (>= 8.0.1)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 8.0.2)
- Microsoft.Extensions.Http (>= 8.0.1)
- Microsoft.Extensions.Http.Resilience (>= 8.10.0)
- Microsoft.Extensions.Logging.Abstractions (>= 8.0.2)
- Microsoft.Extensions.Options (>= 8.0.2)
- Nanoid (>= 3.0.0)
- Ulid (>= 1.3.4)
-
net8.0
- BCrypt.Net-Next (>= 4.0.3)
- Blake3 (>= 2.2.1)
- DocumentFormat.OpenXml (>= 3.2.0)
- FluentValidation (>= 11.10.0)
- FluentValidation.DependencyInjectionExtensions (>= 11.10.0)
- IdGen (>= 3.0.7)
- Mapster (>= 10.0.7)
- Mapster.DependencyInjection (>= 1.0.1)
- Mediator.Abstractions (>= 2.1.7)
- Microsoft.Extensions.Caching.Abstractions (>= 8.0.0)
- Microsoft.Extensions.Caching.Memory (>= 8.0.1)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 8.0.2)
- Microsoft.Extensions.Http (>= 8.0.1)
- Microsoft.Extensions.Http.Resilience (>= 8.10.0)
- Microsoft.Extensions.Logging.Abstractions (>= 8.0.2)
- Microsoft.Extensions.Options (>= 8.0.2)
- Nanoid (>= 3.0.0)
- Ulid (>= 1.3.4)
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 |