Cayaqui.MPS.Abstractions 0.1.0

dotnet add package Cayaqui.MPS.Abstractions --version 0.1.0
                    
NuGet\Install-Package Cayaqui.MPS.Abstractions -Version 0.1.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.1.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.1.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.1.0
                    
#r "nuget: Cayaqui.MPS.Abstractions, 0.1.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.1.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.1.0
                    
Install as a Cake Addin
#tool nuget:?package=Cayaqui.MPS.Abstractions&version=0.1.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

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
0.1.0 105 4/24/2026

Initial release. Entity/AuditableEntity/AggregateRoot, ValueObject, behaviour markers, INamed/ICoded/IDescribed, ITreeNode, ISpecification, PagedRequest, CQRS markers for Wolverine.