Promoted to Evolith: This ADR has been elevated to Evolith ADR-0071 — Domain Layer Base Class and Inheritance Strategy. UMS retains this document as implementation reference.Status:AcceptedDate:2026-05-29Deciders:Architecture TeamSupersedes:ADR-0054 (Shell Library Isolation)
The UMS Domain layer (Ums.Domain) currently inherits from AggregateRoot<T> and Entity<T> base classes provided by BeyondNetCode.Shell.Ddd. This creates a transitive dependency onMediatR (v12.3.0) through the shell library.
Ums.Domain (Project)
└── ProjectReference → BeyondNetCode.Shell.Ddd
└── PackageReference → MediatR (12.3.0)
src/apps/ums.api/Ums.Domain/Authorization/Profile/Profile.cs):public sealed class Profile : AggregateRoot<Profile, ProfileProps>
{
// Inherits: Id, BrokenRules, IsValid(), DomainEvents
}
Shell Ddd Base Class (libs/shell/ddd/src/BeyondNetCode.Shell.Ddd/):
public abstract class AggregateRoot<T, TProps> : Entity<T, TProps>
where TProps : Props
{
public IDomainEvents DomainEvents { get; }
// ... MediatR integration
}
BeyondNetCode.Shell.Ddd base classes.Option B:Refactor to composition-based design, removing inheritance from shell.
Option C:Create a “Domain.Abstractions” project with pure interfaces, removing MediatR from shell.
| # | Pro | Rationale |
|—|—–|———–|
| 1 | Development Velocity | Teams can focus on business logic, not infrastructure boilerplate |
| 2 | Consistent Patterns | All aggregates share identical base behavior (ID, broken rules, events) |
| 3 | MediatR as Infrastructure | MediatR is at the shell level, not in Domain directly - it’s a framework concern |
| 4 | Battle-Tested | Shell libraries are shared across multiple projects (csdevlib) |
| 5 | Less Code to Maintain | No duplicate AggregateRoot<T> implementations needed | #### Cons
| # | Con | Rationale |
|—|—–|———–|
| 1 | Violation of Strict Domain Purity | Domain references external NuGet transitively |
| 2 | Framework Leakage Risk | If shell evolves, Domain may be forced to change |
| 3 | Testing Complexity | Domain tests require MediatR assembly resolution |
| 4 | Conceptual Contamination | Domain concept of “Aggregate” knows about MediatR |
| 5 | Portability Loss | Domain cannot be extracted to separate package | —
| # | Pro | Rationale | |—|—–|———–| | 1 | True Domain Purity | Domain has zero external dependencies | | 2 | Extractable | Domain could be published as standalone NuGet | | 3 | Testability | Pure POCOs easier to unit test | | 4 | No Conceptual Coupling | Domain doesn’t know about MediatR | #### Cons | # | Con | Rationale | |—|—–|———–| | 1 | Massive Refactoring | 100+ entities need modification | | 2 | Lost Consistency | Each aggregate may implement patterns differently | | 3 | Boilerplate Proliferation | Common behavior duplicated across aggregates | | 4 | Breaking Change | Allbounded contexts affected | | 5 | Time Investment | Estimated 3-4 weeks of work | —
Create Ums.Domain.Abstractions with pure interfaces:
IAggregateRoot<T>IEntity<T, TProps>IDomainEventsShell.Ddd implements these interfaces. Domain references only Abstractions.
| # | Pro | Rationale | |—|—–|———–| | 1 | Architectural Clarity | Clear separation between contracts and implementation | | 2 | Domain Purity | Domain only depends on Abstractions (no NuGets) | | 3 | Flexibility | Can swap shell implementation if needed | | 4 | Maintainable | Changes to MediatR don’t ripple to Domain | #### Cons | # | Con | Rationale | |—|—–|———–| | 1 | Another Layer | Adds indirection without immediate benefit | | 2 | Over-Engineering Risk | 3 projects where 2 might suffice | | 3 | Migration Effort | Requires creating Abstractions project and updating references | | 4 | Build Complexity | More projects = longer build times | —
BMAD Rule R-10states: “Domain must be pure POCOs with zero NuGet references” - but this rule isaspirationalin strict interpretation. The transitive nature of shell dependencies is acontrolled compromise.
Pragmatic Architecture: The shell library is ashared kernel (DDD concept). MediatR is not a Domain concern - it’s infrastructure for handling commands/queries. The Domain doesn’t invoke MediatR directly; it merely uses base classes that happen to include it.
Risk Assessment:
Migration path:
Ums.Domain.Abstractions projectIAggregateRoot<T>, IEntity<T>, IDomainEvents interfacesAggregateRoot<T> in shell implement these interfacesShell.Ddd to Shell.Ddd.AbstractionsTo verify Domain purity violation severity, run:
dotnet list src/apps/ums.api/Ums.Domain/Ums.Domain.csproj package
# Should show NO direct package references