Baobab.SharedKernel.Presentation 1.0.0

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

Baobab SharedKernel

.NET License: MIT

A Clean Architecture / Domain-Driven Design foundation for .NET applications of any shape — microservices, modular monoliths, or a single well-organized service. CQRS, an assembly-aware OutBox pattern, comprehensive auditing, and a Result-based error-handling model, all wired together so you can start on business logic instead of infrastructure plumbing, whatever your deployment topology ends up being.

This isn't a scaffold of interfaces waiting to be implemented — every layer below is a working implementation you can reference, extend, or drop into a project today.

Why this exists

Most "Clean Architecture starter" repos give you folder names and a few marker interfaces. This gives you the parts that are actually tedious to get right: an OutBox implementation that won't double-process events when two services share a database, an audit trail that distinguishes background jobs from user requests, a Result type hierarchy that composes across validation and pagination, and Guid generation that stays sortable in the database instead of fragmenting your indexes.

Architecture

SharedKernel/
├── Baobab.SharedKernel.Domain          Entities, aggregates, value objects,
│                                       domain events, the Result pattern
├── Baobab.SharedKernel.Application     CQRS (ICommand/IQuery), pipeline
│                                       behaviors, service abstractions
├── Baobab.SharedKernel.Persistence     EF Core, OutBox pattern, audit
│                                       trail, specifications, unit of work
├── Baobab.SharedKernel.Infrastructure  Caching, background jobs, messaging,
│                                       resilience, external services
└── Baobab.SharedKernel.Presentation    API controllers, Minimal APIs,
                                        exception handling, versioning

Dependencies point inward only: Presentation -> Infrastructure -> Persistence -> Application -> Domain. The Domain layer has zero project dependencies.

What's implemented

  • Domain-Driven Design primitivesAggregateRoot, Entity, EntityExtra (audit fields), ValueObject, with rich value objects (Money, EmailAddress, PhoneNumber, GhanaCardPersonalIdentificationNumber)
  • CQRSICommand/IQuery interfaces over MediatR, with ValidationPipelineBehavior (FluentValidation → Result), LoggingPipelineBehavior, and UnitOfWorkPipelineBehavior running in sequence on every request
  • Result patternResult, ResultT<T>, ValidationResult, PaginatedResult<T> in place of exceptions for expected business outcomes
  • Assembly-aware OutBox pattern — domain events are captured in the same transaction as your data, then published by a background job. The ExecutingAssembly field means multiple services sharing one database only ever process their own events. Idempotent handler decoration means a retried message doesn't re-run side effects.
  • Guid v7 identifiers — every ID is a Guid created via Guid.CreateVersion7(): sortable and time-ordered like a ULID, but a native .NET type with no third-party dependency
  • Audit trailAuditableContext<T> captures before/after values, the acting user, and timestamps automatically on every SaveChangesAsync, with explicit handling for background jobs running without a user context
  • Specification patternHeroSpecification<T> for composable, reusable query logic with includes and ordering
  • Multi-strategy caching — Redis-backed and in-memory implementations of the same ICacheManager interface
  • Background jobs — Hangfire integration with Polly retry policies
  • Messaging — MassTransit + RabbitMQ for integration events
  • Notifications — email (Amazon SES or SMTP), SMS, and push, each with a sync path and a MassTransit-published integration-event path
  • File storage — AWS S3 upload/download behind IAmazonSimpleStorageService
  • API security — JWT bearer authentication, multi-factor API key/secret generation, zone-based authorization layered on top of role checks
  • Observability — OpenTelemetry tracing/metrics across ASP.NET Core, EF Core, Hangfire, gRPC, and Redis, plus Sentry (or self-hosted GlitchTip) error tracking correlated to the same traces
  • Presentation extras — API versioning (controllers and Minimal APIs), Swagger with JWT bearer auth wired in, rate limiting, and an RFC 7807 global exception handler

Getting Started

git clone https://github.com/barimahyaw/baobab.git
cd baobab
dotnet restore Baobab.sln
dotnet build Baobab.sln

NuGet packages aren't published yet — for now, reference the projects directly or pull the SharedKernel/ folder into your solution. See docs/getting-started.md for a full walkthrough of building a service on top of this foundation.

A taste of the patterns

Rich domain model, raising an event:

public class Order : AggregateRoot
{
    public Result AddItem(ProductId productId, Money unitPrice, int quantity)
    {
        if (Status != OrderStatus.Draft)
            return Result.Fail(Errors.OrderErrors.CannotModifyConfirmedOrder);

        var item = OrderItem.Create(Guid.CreateVersion7(), productId, unitPrice, quantity);
        _items.Add(item);

        RaiseDomainEvent(new OrderItemAddedDomainEvent(Id, productId, quantity));
        return Result.Success();
    }
}

CQRS command handler, Result all the way down:

public record CreateUserCommand(string Email, string FirstName) : ICommand<Guid>;

public class CreateUserCommandHandler(AppDbContext dbContext) : ICommandHandler<CreateUserCommand, Guid>
{
    public async Task<IResult<Guid>> Handle(CreateUserCommand request, CancellationToken cancellationToken)
    {
        var emailResult = EmailAddress.Validate(request.Email);
        if (!emailResult.Succeeded) return ResultT<Guid>.Fail(emailResult.Messages);

        var user = User.Create(EmailAddress.Create(request.Email), request.FirstName);
        await dbContext.Users.AddAsync(user, cancellationToken);
        // UnitOfWorkPipelineBehavior calls SaveChangesAsync after the handler returns

        return ResultT<Guid>.Success(user.Id);
    }
}

Minimal API controller:

[ApiVersion("1.0")]
public class UsersController : BaseApiController<UsersController>
{
    [HttpPost]
    public async Task<IActionResult> CreateUser([FromBody] CreateUserCommand command)
    {
        var result = await Mediator.Send(command);
        return result.Succeeded
            ? CreatedAtAction(nameof(GetUser), new { id = result.Value }, result.Value)
            : BadRequest(result.Messages);
    }
}

Documentation

Guide Description
Getting Started Build your first service step-by-step
Architecture Overview The Clean Architecture layers and how they relate
Team Architecture Handoff Guide Complete technical reference, layer by layer
Patterns & Practices Proven patterns with real examples
Practical Examples Complete real-world scenarios
Troubleshooting Common issues and how to resolve them
Full documentation index Everything, one level up

Project Status

This is an actively evolving personal project — the core is stable and used as the foundation for real services, but public NuGet packages and a templated dotnet new experience aren't published yet. Track progress in CHANGELOG.md.

Contributing

Bug reports, documentation fixes, and code contributions are welcome — see CONTRIBUTING.md.

Security

See SECURITY.md for how to report a vulnerability.

License

MIT — see LICENSE.

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

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.0.0 110 9/3/2026