CoreKernel.DomainMarkers 1.0.0

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

CoreKernel.DomainMarkers Library

The CoreKernel.DomainMarkers library provides a set of interfaces that serve as markers for common cross-cutting concerns in domain entities. These markers help standardize entity behaviors across your application while enabling automatic handling of concerns like auditing, multi-tenancy, soft deletion, and distributed tracing.

Table of Contents


Overview

The CoreKernel.DomainMarkers project encapsulates essential cross-cutting concerns as reusable marker interfaces. It enables consistent modeling of entity behaviors across an application, promoting maintainability and reducing boilerplate code. These markers can be combined to implement multiple aspects in a single entity.


Features

  • Auditing

    • Track when entities are created and modified
    • Record which users performed entity changes
  • Multi-Tenancy

    • Scope entities to specific tenants in multi-tenant applications
    • Type-safe tenant identifier tracking
  • Soft Deletion

    • Mark entities as deleted without physical removal
    • Track when and by whom entities were deleted
  • Tracing

    • Maintain correlation IDs across distributed systems
    • Record operation sources and names for troubleshooting

Installation

Add the CoreKernel.DomainMarkers package to your project:

dotnet add package CoreKernel.DomainMarkers

Usage

Auditing

The auditing interfaces provide mechanisms to track entity creation and modification:

// Basic time tracking
public class BlogPost : Entity<Guid>, ITimeStamped
{
    public string Title { get; set; }
    public string Content { get; set; }
    
    // ITimeStamped implementation
    public DateTimeOffset CreatedOn { get; set; }
    public DateTimeOffset LastModifiedOn { get; set; }
}

// Complete auditing with user tracking
public class Invoice : Entity<Guid>, IAuditable
{
    public decimal Amount { get; set; }
    public string CustomerName { get; set; }
    
    // IAuditable implementation
    public DateTimeOffset CreatedOn { get; set; }
    public string CreatedBy { get; set; }
    public DateTimeOffset LastModifiedOn { get; set; }
    public string LastModifiedBy { get; set; }
}

Multi-Tenancy

The ITenantScoped<TId> interface enables multi-tenant data isolation:

public class CustomerRecord : Entity<Guid>, ITenantScoped<Guid>
{
    public string CustomerName { get; set; }
    public string ContactEmail { get; set; }
    
    // ITenantScoped implementation
    public Guid TenantId { get; set; }
}

This allows for automatic tenant filtering in queries and prevents cross-tenant data access.


Soft Deletion

The ISoftDeletable interface supports logical deletion of entities:

public class Document : Entity<Guid>, ISoftDeletable
{
    public string Title { get; set; }
    public string Content { get; set; }
    
    // ISoftDeletable implementation
    public bool IsDeleted { get; set; }
    public DateTimeOffset? DeletedOn { get; set; }
    public string? DeletedBy { get; set; }
    
    public void Delete(string userId)
    {
        IsDeleted = true;
        DeletedOn = DateTimeOffset.UtcNow;
        DeletedBy = userId;
    }
}

Tracing

The ITraceable interface enables distributed tracing across systems:

public class Payment : Entity<Guid>, ITraceable
{
    public decimal Amount { get; set; }
    public string PaymentMethod { get; set; }
    
    // ITraceable implementation
    public Guid CorrelationId { get; set; }
    public string? TraceSource { get; set; }
    public string? OperationName { get; set; }
}

Integration Examples

Repository Implementations

Domain markers can be leveraged in generic repository implementations:

public class GenericRepository<TEntity, TId> : IRepository<TEntity, TId>
    where TEntity : Entity<TId>
    where TId : notnull
{
    private readonly DbContext _dbContext;
    private readonly ICurrentUserService _currentUser;
    
    public GenericRepository(DbContext dbContext, ICurrentUserService currentUser)
    {
        _dbContext = dbContext;
        _currentUser = currentUser;
    }
    
    public async Task SaveAsync(TEntity entity)
    {
        // Handle audit information
        if (entity is IAuditable auditable)
        {
            var now = DateTimeOffset.UtcNow;
            var userId = _currentUser.UserId ?? "system";
            
            if (IsNew(entity))
            {
                auditable.CreatedOn = now;
                auditable.CreatedBy = userId;
            }
            
            auditable.LastModifiedOn = now;
            auditable.LastModifiedBy = userId;
        }
        else if (entity is ITimeStamped timeStamped)
        {
            var now = DateTimeOffset.UtcNow;
            
            if (IsNew(entity))
            {
                timeStamped.CreatedOn = now;
            }
            
            timeStamped.LastModifiedOn = now;
        }
        
        // Save entity logic...
        await _dbContext.SaveChangesAsync();
    }
    
    private bool IsNew(TEntity entity)
    {
        return !_dbContext.Entry(entity).IsKeySet;
    }
}

Middleware and Filters

Domain markers can be utilized in middleware or filters for automatic handling:

public class AuditingMiddleware<TRequest, TResponse>
    : IPipelineBehavior<TRequest, TResponse>
    where TRequest : IRequest<TResponse>
{
    private readonly ICurrentUserService _currentUserService;
    
    public AuditingMiddleware(ICurrentUserService currentUserService)
    {
        _currentUserService = currentUserService;
    }
    
    public async Task<TResponse> Handle(TRequest request, RequestHandlerDelegate<TResponse> next, CancellationToken cancellationToken)
    {
        // Apply auditing to command handlers that modify IAuditable entities
        if (request is IEntityCommand command && command.Entity is IAuditable auditable)
        {
            var now = DateTimeOffset.UtcNow;
            var userId = _currentUserService.UserId ?? "system";
            
            if (command.IsNew)
            {
                auditable.CreatedOn = now;
                auditable.CreatedBy = userId;
            }
            
            auditable.LastModifiedOn = now;
            auditable.LastModifiedBy = userId;
        }
        
        return await next();
    }
}

Database Filters

Entity Framework Core global query filters can leverage the domain markers:

public class ApplicationDbContext : DbContext
{
    private readonly ITenantProvider _tenantProvider;
    
    public ApplicationDbContext(DbContextOptions options, ITenantProvider tenantProvider) 
        : base(options)
    {
        _tenantProvider = tenantProvider;
    }
    
    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);
        
        // Apply soft delete filter
        foreach (var entityType in modelBuilder.Model.GetEntityTypes())
        {
            if (typeof(ISoftDeletable).IsAssignableFrom(entityType.ClrType))
            {
                var parameter = Expression.Parameter(entityType.ClrType, "e");
                var property = Expression.Property(parameter, nameof(ISoftDeletable.IsDeleted));
                var falseConstant = Expression.Constant(false);
                var expression = Expression.Equal(property, falseConstant);
                var lambda = Expression.Lambda(expression, parameter);
                
                modelBuilder.Entity(entityType.ClrType).HasQueryFilter(lambda);
            }
            
            // Apply multi-tenancy filter for Guid tenant IDs
            if (typeof(ITenantScoped<Guid>).IsAssignableFrom(entityType.ClrType))
            {
                var parameter = Expression.Parameter(entityType.ClrType, "e");
                var property = Expression.Property(parameter, nameof(ITenantScoped<Guid>.TenantId));
                var tenantId = Expression.Constant(_tenantProvider.GetCurrentTenantId());
                var expression = Expression.Equal(property, tenantId);
                var lambda = Expression.Lambda(expression, parameter);
                
                modelBuilder.Entity(entityType.ClrType).HasQueryFilter(lambda);
            }
        }
    }
}

Best Practices

  1. Combine Domain Markers - Use multiple interfaces on an entity to address all relevant cross-cutting concerns.
  2. Centralize Implementation Logic - Handle the marker interfaces in middleware, repositories, or database context rather than duplicating logic.
  3. Consider Performance - Be mindful of how filters affect query performance, especially with complex combinations of markers.
  4. Isolation of Concerns - Use markers to maintain a clean separation between domain logic and cross-cutting concerns.
  5. Automatic Handling - Implement automatic handling of domain markers in infrastructure layers to minimize boilerplate code.
  6. Consistent Application - Apply the same markers consistently across related entities.
  7. Type Safety - Leverage generic type parameters for tenant and other IDs to ensure type safety.
  8. Don't Mix Domain Logic - Keep the marker interfaces focused on infrastructure concerns and separate from domain logic.
  9. Include in Base Classes - Consider creating base entity classes that implement common combinations of markers.
  10. Documentation - Clearly document which markers an entity implements to make the cross-cutting concerns explicit.
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 is compatible.  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.
  • net8.0

    • No dependencies.
  • net9.0

    • No dependencies.

NuGet packages (1)

Showing the top 1 NuGet packages that depend on CoreKernel.DomainMarkers:

Package Downloads
CoreKernel

CoreKernel is a modular .NET library that provides reusable primitives, functional utilities, domain markers, and messaging interfaces for building clean, maintainable, and domain-driven applications.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.0.0 251 5/12/2025