Tipo: DDD — C# Domain Primitives
Version: 2.0 | Fecha: 2026-05-15
ADR de referencia: ADR-0054 — Shell Library Isolation for DDD and Factory Patterns
UMS implementa sus primitivas DDD mediante librerías shell propias bajo src/libs/shell. Estas librerías encapsulan patrones heredados y los exponen con namespace UMS, evitando que el dominio de producto dependa directamente de nombres o estructuras externas.
La regla vigente no es “no usar librerías”. La regla vigente es:
Ums.Domainpuede depender deUms.Shell.*, pero no puede depender directamente de librerías externas, infraestructura, persistencia, brokers, frameworks web ni SDKs de proveedor.
flowchart TD
Domain["Ums.Domain<br/>Modelo de dominio UMS"] --> Ddd["Ums.Shell.Ddd<br/>Primitivas DDD"]
Domain --> ValueObjects["Ums.Shell.Ddd.ValueObjects<br/>Value Objects reutilizables"]
Domain --> Factory["Ums.Shell.Factory<br/>Factory pattern"]
ValueObjects --> Ddd
Ddd --> Factory
UpstreamDdd["csdevlib/ddd"] -. "adaptado, renombrado y aislado" .-> Ddd
UpstreamDdd -. "adaptado, renombrado y aislado" .-> ValueObjects
UpstreamFactory["csdevlib/factory-pattern"] -. "adaptado, renombrado y aislado" .-> Factory
| Librería | Rol funcional | Uso en UMS |
|---|---|---|
Ums.Shell.Ddd |
Encapsula patrones DDD tácticos: entidad, agregado, eventos de dominio, validación, especificaciones y reglas base. | Base para modelar agregados y comportamiento de dominio. |
Ums.Shell.Ddd.ValueObjects |
Centraliza patrones reutilizables de value objects. | Evita duplicación y asegura igualdad/validación consistente. |
Ums.Shell.Factory |
Encapsula el patrón Factory y soporte de resolución/creación. | Usado por Ums.Shell.Ddd y por dominio cuando se requiera creación controlada. |
// Entity base con identidad y domain events
public abstract class Entity<TId>
{
public TId Id { get; protected set; }
private readonly List<IDomainEvent> _events = new();
public IReadOnlyList<IDomainEvent> DomainEvents => _events.AsReadOnly();
protected void Raise(IDomainEvent e) => _events.Add(e);
public void ClearDomainEvents() => _events.Clear();
}
// Aggregate Root marca el limite de consistencia
public abstract class AggregateRoot<TId> : Entity<TId> { }
// Value Object: igualdad estructural, inmutabilidad
public abstract record ValueObject;
// Result pattern: sin excepciones en flujos de negocio
public readonly struct Result<T>
{
public bool IsSuccess { get; }
public T Value { get; }
public string Error { get; }
public static Result<T> Ok(T value) => new(true, value, null!);
public static Result<T> Fail(string error) => new(false, default!, error);
}
public interface IDomainEvent
{
Guid EventId { get; }
DateTimeOffset OccurredAt { get; }
string EventType { get; }
}
src/
apps/
app-api-dotnet/
Ums.Domain/
Identity/
Aggregates/ <- Tenant.cs, UserAccount.cs, Branch.cs
ValueObjects/ <- Email.cs, UserCategory.cs, TenantType.cs ...
Events/ <- UserRegisteredEvent.cs, UserActivatedEvent.cs ...
Repositories/ <- ITenantRepository.cs, IUserAccountRepository.cs
Authorization/
Aggregates/ <- SystemSuite.cs, Role.cs, PermissionTemplate.cs, Profile.cs
ValueObjects/ <- ExclusiveArcTarget.cs, PermissionEffect.cs ...
Events/ <- ProfileCreatedEvent.cs, PermissionMutatedEvent.cs ...
Repositories/ <- IProfileRepository.cs, IRoleRepository.cs
Configuration/
Approvals/
IGA/
Compliance/
Audit/
libs/
shell/
ddd/
src/
Ums.Shell.Ddd/
Ums.Shell.Ddd.ValueObjects/
factory/
src/
Ums.Shell.Factory/
Las carpetas de dominio por contexto se mantienen enfocadas en reglas de negocio. Las primitivas compartidas viven en libs/shell y se consumen por referencia de proyecto.
Ums.Domain/
Identity/
Aggregates/ <- Tenant.cs, UserAccount.cs, Branch.cs
ValueObjects/ <- Email.cs, UserCategory.cs, TenantType.cs ...
Events/ <- UserRegisteredEvent.cs, UserActivatedEvent.cs ...
Repositories/ <- ITenantRepository.cs, IUserAccountRepository.cs
Authorization/
Aggregates/ <- SystemSuite.cs, Role.cs, PermissionTemplate.cs, Profile.cs
ValueObjects/ <- ExclusiveArcTarget.cs, PermissionEffect.cs ...
Events/ <- ProfileCreatedEvent.cs, PermissionMutatedEvent.cs ...
Repositories/ <- IProfileRepository.cs, IRoleRepository.cs
Configuration/
Approvals/
IGA/
Compliance/
Audit/
| Regla | Detalle |
|---|---|
| Primitivas via shell | Entity, AggregateRoot, ValueObject, Result y patrones relacionados se consumen desde Ums.Shell.* cuando aplique |
| Sin dependencia externa directa | Ums.Domain no puede depender directamente de paquetes o namespaces externos para patrones tácticos; debe pasar por Ums.Shell.* |
| Namespace propio | No se permite BeyondNet.*, csdevlib.* u otro namespace heredado en código de aplicación UMS |
| Runtime único | API y shell libraries compilan sobre net10.0 estable |
| Factory encapsulado | El uso de Factory pattern queda centralizado en Ums.Shell.Factory y puede ser reutilizado por Ums.Shell.Ddd |
| Value Objects son records | Igualdad estructural automatica via record; inmutabilidad via propiedades init |
| IDs como Guid | Todos los IDs son Guid; nunca int ni string en Aggregate Roots |
| Composite PK en persistencia | (Id, RootTenantId) en todas las tablas; mandatorio para RLS + particionado |
La adopción de shell libraries exige que estas compilaciones pasen antes de promover cambios:
dotnet build src/apps/app-api-dotnet/Ums.Presentation/Ums.Presentation.csproj
dotnet build src/libs/shell/factory/src/Ums.Shell.Factory.sln
dotnet build src/libs/shell/ddd/src/Ums.Shell.Ddd/Ums.Shell.Ddd.sln
| Anterior: Cross-Context Flows | Indice DDD | Siguiente: Design Decisions |