Hmz.Core.SharedKernel 0.1.9

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

Hmz.Core.SharedKernel (SharedKernel)

The shared kernel library for the Hmz ecosystem providing core abstractions, utilities, and domain building blocks that serve as the foundation for all Hmz projects.

Overview

This project contains reusable patterns and utilities based on Domain-Driven Design (DDD) principles and Clean Architecture. It provides a consistent foundation for building domain-driven applications within the Hmz family of projects.

Features

πŸ›‘οΈ Guard Clauses & Validation

Leverage Ardalis.GuardClauses for concise and expressive input validation:

Guard.Against.Null(input);
Guard.Against.NullOrEmpty(text);
Guard.Against.OutOfRange(age, min: 0, max: 120);

πŸ“‹ Result Pattern

Use the Hmz-owned structured Result contract for expected application failures:

using Hmz.Core.SharedKernel.Results;

public Result<User> CreateUser(string email)
{
    var validationErrors = new List<Error>();

    if (string.IsNullOrWhiteSpace(email))
    {
        validationErrors.Add(Error.Validation(
            "user.email_required",
            "Email is required.",
            "email"));
    }

    return validationErrors.Count > 0
        ? Result.Failure<User>(validationErrors)
        : Result.Success(new User(email));
}

Errors carry a stable code, a safe human-readable message, a transport-neutral category, and an optional validation target. A successful generic result always contains a non-null value. A failed result may contain multiple errors of the same category. Use Match, Map, and Bind to compose successful operations without losing structured failures.

Error codes use the diagnostic format {app}.{type-prefix}.{app-error-code}. Generate catalog values with ErrorCode.Create; because the method runs at runtime, catalogs expose generated values as static readonly rather than const:

public static readonly string ValidationFailed = ErrorCode.Create(
    ErrorType.Validation,
    "failed");

// COM.VAL.FAILED

The default application namespace is COM. Keep application/module prefixes short and stable (for example, UM for User Management or CM for Catalog Management), not long display names. Use hyphen-separated semantic suffixes and do not repeat the type meaning: prefer CM.NOTF.PRODUCT-ITEMS over CATALOGMANAGEMENT.NOTF.PRODUCT.NOT-FOUND.

Use the application-first overload when a feature belongs to a specific application:

var code = ErrorCode.Create(
    "Billing",
    ErrorType.Conflict,
    "invoice.duplicate");

// BILLING.CONF.INVOICE.DUPLICATE

The contract combines the practical strengths of:

Only expected failures use Result. Unexpected infrastructure and programming failures remain exceptions and are handled by the application's exception boundary. HTTP status codes, Problem Details mapping, and strong row-version ETags are available under Hmz.Core.SharedKernel.Http. The helpers keep transport conventions consistent across APIs while leaving domain errors transport-neutral.

ETags use canonical quoted invariant decimal row versions such as "42". Clients should treat the complete quoted value as opaque and send it back in If-Match or If-None-Match unchanged.

Pagination

Use the same pagination response contract across modules and services:

using Hmz.Core.SharedKernel.Contracts.Pagination;

return new PagedResponse<OrderResponse>(items, page, pageSize, totalCount);

πŸ” Specification Pattern

Build complex queries in a domain-driven way with Ardalis.Specification and Entity Framework Core integration:

public class ActiveUserSpec : Specification<User>
{
    public ActiveUserSpec()
    {
        Query.Where(u => u.IsActive);
    }
}

var users = await repository.ListAsync(new ActiveUserSpec());

🏷️ Smart Enums

Type-safe enumerations with Ardalis.SmartEnum:

public class UserStatus : SmartEnum<UserStatus>
{
    public static readonly UserStatus Active = new("Active", 1);
    public static readonly UserStatus Inactive = new("Inactive", 2);

    private UserStatus(string name, int value) : base(name, value) { }
}

πŸ”§ Startup Services Management

Introspect and list registered services with Ardalis.ListStartupServices.

🌐 ASP.NET Core Integration

ASP.NET Core Result-to-Problem-Details mapping is intentionally kept outside this transport-neutral package.

Malformed request binding is handled by Hmz.Core.SharedKernel.Http at the API boundary. RequestBodyExceptionHandler keeps ordinary failures generic (The request is invalid.) and exposes only messages explicitly marked safe by Hmz.Core.SharedKernel.Serialization.SafeJsonException. The advanced-search operator, search-logic, and sort-direction converters use that marker to include accepted values without exposing framework binding details. Their request properties apply the converters explicitly so a host-level catch-all JsonStringEnumConverter cannot replace the safe converters.

Project Setup

Configuration

  • Target Framework: .NET 10
  • C# Language Version: Latest (preview)
  • Nullable Reference Types: Enabled
  • Implicit Usings: Enabled
  • Treat Warnings as Errors: Enabled

Package Information

  • Package ID: Hmz.Core.SharedKernel
  • Target: Distributed as a NuGet package for use across Hmz projects

Usage

In a .NET Project

  1. Add package reference:
dotnet add package Hmz.Core.SharedKernel
  1. Use the utilities:
using Ardalis.GuardClauses;
using Hmz.Core.SharedKernel.Results;

public class OrderService
{
    public Result<Order> CreateOrder(OrderRequest request)
    {
        Guard.Against.Null(request);
        Guard.Against.NullOrEmpty(request.CustomerId);

        var order = new Order(request.CustomerId);
        return Result.Success(order);
    }
}

Dependencies

All dependencies are managed through Directory.Packages.props at the solution level:

  • Ardalis.GuardClauses - Guard clauses and validation
  • Ardalis.ListStartupServices - Service introspection
  • Ardalis.SharedKernel - Shared kernel abstractions
  • Ardalis.SmartEnum - Smart enumerations
  • Ardalis.Specification - Specification pattern
  • Ardalis.Specification.EntityFrameworkCore - EF Core integration

Best Practices

When extending the SharedKernel:

  1. Validation First: Always validate inputs using Guard clauses

    Guard.Against.NullOrEmpty(name, nameof(name));
    
  2. Consistent Error Handling: Use Result pattern for operations that can fail

    return Result.Failure(
        Error.NotFound("user.not_found", "User not found."));
    
  3. Domain-Driven Queries: Leverage Specification pattern for complex queries

    var spec = new UsersByRoleSpec(role);
    var users = await repository.ListAsync(spec);
    
  4. Type-Safe Enums: Use SmartEnum for status and state enumerations

    if (status == OrderStatus.Pending)
    {
        // Handle pending order
    }
    

Building and Testing

Build the Project

dotnet build

Run Tests

dotnet run --project tests/Hmz.Core.SharedKernel.UnitTests/Hmz.Core.SharedKernel.UnitTests.csproj -- --no-ansi --progress off

The SharedKernel unit tests use xUnit v3 with AwesomeAssertions, FakeItEasy, and Microsoft.Testing.Platform. The test project is an executable test host and is marked as non-packable and non-publishable.

Create NuGet Package

dotnet pack --configuration Release

Architecture

The SharedKernel follows these architectural principles:

  • Domain-Driven Design (DDD): Core domain concepts are clearly expressed
  • Clean Architecture: Separation of concerns with clear boundaries
  • SOLID Principles: Adherence to design principles for maintainability
  • Reusability: Components are designed to be used across multiple projects

Contributing

When adding new features to the SharedKernel:

  1. Ensure compatibility with .NET 10
  2. Follow the existing patterns and conventions
  3. Add proper guard clauses and validation
  4. Use the Result pattern for error handling
  5. Document public APIs thoroughly
  6. Update this README if adding major features

Support

For questions or issues regarding the SharedKernel, please refer to the main Hmz.Core documentation or contact the Hmz development team.

Hmz.Core.SharedKernel.AdvancedSearch.OpenApi also provides the reusable AdvancedSearchOpenApiMetadata and AdvancedSearchOpenApiTransformer types. Register the transformer once at the API host and keep endpoint-specific examples in the feature that owns its ISearchConfiguration.

Hmz.Core.SharedKernel.AdvancedSearch provides a reusable, allow-listed EF Core list-query pipeline. The client sends structured global-search groups, field filters, strongly typed sort instructions, and pagination. Raw Dynamic LINQ expressions and delimiter-based sort strings are never accepted from the client.

Register the stateless shared services once:

services.AddAdvancedSearch();

The preferred application flow is:

AdvancedSearchRequest
        ↓
IAdvancedSearchService
  β”œβ”€ validates and normalizes against ISearchConfiguration
  β”œβ”€ resolves optional external values
  β”œβ”€ applies Dynamic LINQ only to allow-listed members
  └─ counts, pages, and optionally projects
        ↓
PagedResponse<TResult>

A feature supplies only its ISearchConfiguration, source query, result projection, and optional external resolvers:

public sealed class ProductSearchQuery(
    AppDbContext dbContext,
    IAdvancedSearchService advancedSearch)
    : IAdvancedSearchQuery<ProductListItem>
{
    public ValueTask<PagedResponse<ProductListItem>> ExecuteAsync(
        AdvancedSearchRequest request,
        CancellationToken cancellationToken = default) =>
        advancedSearch.ExecutePageAsync(
            dbContext.Products.AsNoTracking(),
            product => new ProductListItem(product.Id, product.Name, product.Price),
            request,
            ProductSearchConfiguration.Instance,
            cancellationToken: cancellationToken);
}

IAdvancedSearchQuery<TResult> is the generic adapter contract; features do not need one interface per entity. The projection overload applies Where, OrderBy, Count, Skip, and Take to the source entity/query shape before constructing the DTO. Configure SearchFieldDefinition.MatchKey when the client-visible field name differs from the source member used by EF Core.

Important semantics:

  • fields inside one GlobalSearchItem are OR-ed;
  • separate global-search items use SearchLogic;
  • filters default to AND, while multiple positive values inside one filter are OR-ed;
  • negative multi-value operators are AND-ed;
  • external no-match results remain match-nothing rather than becoming an unconstrained query;
  • default ordering should end with a unique field for deterministic pagination;
  • AdvancedSearchValidationException.Errors uses exact paths such as search[0].searchFields[1], filter.filters[0].searchValues[0], and orderBy[0].direction.
  • search operators accept compact codes such as CT/EQ and readable aliases such as contains/equals, case-insensitively;
  • search logic accepts AND/OR case-insensitively and reports the bounded accepted values when invalid input is supplied;
  • boolean values are invariant strings true or false; invalid scalar values include accepted formats in their validation message;
  • call AddHmzProblemDetails() and UseExceptionHandler() in the host so malformed request-binding failures are returned as sanitized HTTP 400 validation problem details with the stable errors.request key and generic message The request is invalid. instead of HTTP 500; explicitly safe converters may provide bounded accepted-value guidance. The search-logic and sort-direction converters are applied directly to their request properties so they are not shadowed by a host-level catch-all JsonStringEnumConverter;

The source intentionally uses normal arrays (new[] { value } / Array.Empty<T>()) at public collection boundaries. This avoids compiler-generated single-element collection helper types appearing in decompiled package APIs.

See the template repository's docs/ADVANCED_SEARCH_GUIDE.md for the full request contract and endpoint example.

Product Compatible and additional computed target framework versions.
.NET 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 (1)

Showing the top 1 NuGet packages that depend on Hmz.Core.SharedKernel:

Package Downloads
Hmz.Core.CrudKit

CRUD toolkit for Hmz projects, built on Hmz.Core.SharedKernel

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.1.9 99 9/12/2026
0.1.8 106 9/7/2026
0.1.7 130 5/8/2026
0.1.6 118 5/7/2026
0.1.5 108 5/6/2026
0.1.4 114 4/23/2026
0.1.3 124 4/22/2026
0.1.1 124 4/20/2026
0.1.0 125 3/22/2026