Cayaqui.MPS.Abstractions 0.2.0

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

Cayaqui.MPS.Abstractions

Zero-dependency domain contracts for .NET 10 MPS modules. Contiene los tipos base que el resto de los módulos del dominio implementan/heredan.

Contenido

Categoría Tipos
Entity base IEntity<TId> · Entity<TId> · AuditableEntity<TId> · AggregateRoot<TId>
Behaviour markers IAuditable · ISoftDeletable · IVersioned
Lookup markers INamed · ICoded · IDescribed
Value Object ValueObject (clásica con GetEqualityComponents)
Domain Events IDomainEvent · IHasDomainEvents · DomainEvent (record base)
Tree / WBS 🌲 ITreeNode<TSelf, TId> + TreeNodeExtensions (Traverse, FindPath)
Specification ISpecification<T> · Specification<T> (criteria + includes + order + paging)
Requests PagedRequest · SortedRequest · SearchRequest · SortOrder enum
CQRS markers ICommand · ICommand<TResult> · IQuery<TResult>

Distribución propietaria — requiere contrato comercial con Cayaqui. Ver LICENSE.txt.

Instalación

dotnet add package Cayaqui.MPS.Abstractions

Patrón típico

Entidad con Id tipado y audit fields

public sealed class ControlAccount : AuditableEntity<Guid>, ICoded, INamed
{
    public string Code { get; private set; }
    public string Name { get; private set; }

    private ControlAccount() { }
    public ControlAccount(Guid id, string code, string name) : base(id)
    {
        Code = code;
        Name = name;
    }
}

Aggregate root con domain events

public sealed class Project : AggregateRoot<Guid>
{
    public ProjectStatus Status { get; private set; }

    public void Approve()
    {
        if (Status != ProjectStatus.Draft)
            throw new InvalidOperationException("Only drafts can be approved.");
        Status = ProjectStatus.Approved;
        RaiseDomainEvent(new ProjectApprovedEvent(Id));
    }
}

public sealed record ProjectApprovedEvent(Guid ProjectId) : DomainEvent;

Con Wolverine, los eventos se despachan desde un SaveChangesInterceptor:

var aggregates = context.ChangeTracker
    .Entries<IHasDomainEvents>()
    .Where(e => e.Entity.DomainEvents.Any())
    .Select(e => e.Entity).ToList();

foreach (var agg in aggregates)
{
    foreach (var ev in agg.DomainEvents) await bus.PublishAsync(ev);
    agg.ClearDomainEvents();
}

ValueObject clásica

public sealed class Money : ValueObject
{
    public decimal Amount { get; }
    public string Currency { get; }

    public Money(decimal amount, string currency) { Amount = amount; Currency = currency; }

    protected override IEnumerable<object?> GetEqualityComponents()
    {
        yield return Amount;
        yield return Currency;
    }
}

Para un Money listo para usar incluyendo arithmetic y validación ISO 4217, ver Cayaqui.MPS.Helpers.

Specification

public sealed class ActiveControlAccountsByProject : Specification<ControlAccount>
{
    public ActiveControlAccountsByProject(Guid projectId)
    {
        Where(ca => ca.ProjectId == projectId && !ca.IsDeleted);
        AddInclude(ca => ca.WorkPackages);
        ApplyOrderBy(ca => ca.Code);
        DisableTracking();
    }
}

// en el handler / repository:
var spec = new ActiveControlAccountsByProject(projectId);
var list = await db.ControlAccounts.Apply(spec).ToListAsync(ct);

(El método Apply es un evaluador típico: itera spec.Includes/IncludeStrings, aplica Where, OrderBy/OrderByDescending, Skip/Take y AsNoTracking.)

WBS / hierarchical breakdown

public sealed class WbsNode : Entity<Guid>, ITreeNode<WbsNode, Guid>
{
    public Guid? ParentId { get; private set; }
    public int Level { get; private set; }
    public string Path { get; private set; } = "";
    public IReadOnlyCollection<WbsNode> Children { get; private set; } = Array.Empty<WbsNode>();
}

// enumerar descendientes en pre-orden:
var all = root.Traverse<WbsNode, Guid>().ToList();

// path ancestral hasta un nodo:
var pathToTarget = root.FindPath<WbsNode, Guid>(n => n.Code == "1.2.3");

CQRS markers con Wolverine

public sealed record CreateControlAccountCommand(Guid ProjectId, string Code) : ICommand<Guid>;
public sealed record GetControlAccountByIdQuery(Guid Id) : IQuery<ControlAccountDto>;

// Wolverine descubre el handler por convención:
public static class ControlAccountHandlers
{
    public static async Task<Guid> Handle(CreateControlAccountCommand cmd, AppDbContext db) { ... }
    public static async Task<ControlAccountDto?> Handle(GetControlAccountByIdQuery q, AppDbContext db) { ... }
}

Paged request

public sealed record ListControlAccountsQuery : SearchRequest, IQuery<PaginatedList<ControlAccountDto>>
{
    public Guid ProjectId { get; init; }
}

// cliente:
var query = new ListControlAccountsQuery
{
    ProjectId = projId,
    PageNumber = 2,
    PageSize = 50,
    SortBy = "Code",
    SortOrder = SortOrder.Ascending,
    SearchTerm = "civil"
};

Diseño

  • Zero dependencies — puro .NET 10 BCL. Cualquier consumidor puede tomarlo sin arrastrar EF Core, MediatR, Wolverine, etc.
  • Sin opinión de persistencia — los markers y bases son agnósticos. EF Core se configura con HasQueryFilter para ISoftDeletable, IsRowVersion para IVersioned, interceptores para IAuditable.
  • Equality predecibleEntity (por Id + tipo runtime), ValueObject (estructural), con hashes deterministas.
  • Compatible con Wolverine — los CQRS markers son opcionales; Wolverine descubre handlers por convención sin requerirlos, pero hacer el marcado explícito mejora la intención en código.

Requisitos

  • .NET 10.0 o superior
  • Sin dependencias externas
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.
  • net10.0

    • No dependencies.

NuGet packages (4)

Showing the top 4 NuGet packages that depend on Cayaqui.MPS.Abstractions:

Package Downloads
Cayaqui.MPS.BuildingBlocks.EntityFrameworkCore

EF Core integration for Cayaqui.MPS.BuildingBlocks: transactional Outbox (EfOutboxStore with Postgres FOR UPDATE SKIP LOCKED + redispatch/purge hosted services + multi-store health check), audit + domain-event-dispatch SaveChanges interceptors, and Blazor Server-safe scoped CQRS handlers (ScopedQueryHandler/ScopedCommandHandler over IDbContextFactory returning Result<T>). Proprietary — requires a commercial agreement with Cayaqui.

Cayaqui.MPS.Cqrs

CQRS handler-pipeline decorators (logging) for the MPS IHandler. Contains LoggingHandlerDecorator<TIn, TResult> which wraps any IHandler<TIn, Result<TResult>> with structured logging of entry/success/failure/exceptions. Kept separate from Cayaqui.MPS.Abstractions so that the zero-dependency core is preserved. Proprietary — requires a commercial agreement with Cayaqui.

Cayaqui.MPS.Abstractions.Validation

FluentValidation cross-cutting decorator for the MPS CQRS IHandler pipeline. Wraps any IHandler<TIn, Result<TResult>> with an optional IValidator<TIn>; on validation failure returns a typed DomainError (ErrorType.Validation) with per-field Metadata — no control-flow exceptions. Kept as a separate package so FluentValidation is not forced on zero-dep consumers of Cayaqui.MPS.Abstractions. Proprietary — requires a commercial agreement with Cayaqui.

Cayaqui.MPS.Authorization

Business authorization layer for MPS Blazor apps: project-scoped roles and granular permissions, AppUser entity, MpsRoles/MpsPermissions constants, IMpsCurrentUser, MpsAuthorizeView and MpsAccessDenied Blazor components, and AuthorizationDbContext (EF Core). Supports both Entra ID and local auth deployment models.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.2.0 150 6/4/2026
0.1.0 124 4/24/2026

0.2.0 — BREAKING: namespaces folder-matched (tipos movidos a sub-namespaces Entities/Messaging/Requests/Specifications; los consumidores deben actualizar usings). Agrega CQRS: IHandler<,> (MPS.Abstractions.Cqrs) y PagedResult<T>. LoggingHandlerDecorator movido a Cayaqui.MPS.Cqrs para restaurar zero-dependency. 0.1.0 — Initial release: Entity/AuditableEntity/AggregateRoot, ValueObject, behaviour markers, INamed/ICoded/IDescribed, ITreeNode, ISpecification, PagedRequest, CQRS markers.