BitzOrcas.Domain is the zero-dependency foundation every other building block and module composes against. It ships nothing runtime-active — no DI registrations, no middleware, no hosted services — just shared abstractions every domain layer needs: entity bases, aggregate-root markers, the Result<T> error-handling pattern, domain events, value objects, and tenant interfaces.
What it ships
Entity bases
Entity<TId>— abstract; carriesId(with Yitter snowflake generation by default), a private domain-event list surfaced asDomainEvents, and protected methodsAddDomainEvent()/ClearDomainEvents(). Every persisted entity inherits from this.AggregateRoot<TId>— extendsEntity<TId>; a semantic marker for aggregate roots — consistency boundaries. Adds no additional members today, but gives you a place for aggregate-wide helpers.IEntity<TId>— the minimal entity contract:TId Id { get; }.
Cross-cutting interfaces
ITenantEntity—TenantId TenantId { get; }. Implemented byTenantEntityBase; marks entities as tenant-scoped. SqlSugar’s global query filter uses this to enforce multi-tenancy.IAuditableEntity—CreatedAt,CreatedBy,LastModifiedAt,LastModifiedBy. Automatically populated by SqlSugar’s audit interceptor.ISoftDelete— soft-delete marker. Entities implementing this are hidden by default through SqlSugar global filters; opt-out with explicit queries.IConcurrencyTracked— optimistic concurrency tracking via row version.
Result<T> pattern
BitzOrcas uses a Result<T> monad instead of throwing exceptions for domain failures:
| Type | Purpose |
|---|---|
Result<T> | Wraps a success value or an Error |
Error | Structured error with ErrorType, code, and description |
ErrorType | Enum: Validation, NotFound, Conflict, Unauthorized, Forbidden, Failure, Infrastructure |
// Domain service returns Result instead of throwingpublic Result<Note> CreateNote(string title, string content){ if (string.IsNullOrWhiteSpace(title)) return Result<Note>.Failure(Error.Validation("Note.Title", "Title is required"));
var note = new Note(title, content); return Result<Note>.Success(note);}Domain events
DomainEvent is an abstract record dispatched automatically by the Mediator pipeline (DomainEventDispatchPipelineBehavior):
public record NoteCreatedDomainEvent(Guid NoteId, string Title) : DomainEvent;Events are collected in the aggregate and dispatched after the transaction commits — no SaveChangesInterceptor needed.
Value objects
Value objects provide type-safe primitives:
NoteTitle— non-empty string with max-length constraintEmailAddress— validated email formatValueObject— base class with equality-by-value semantics
Tenant abstractions
TenantId— strongly-typed tenant identifier (wrapsstring)TenancyMode— enum:SingleTenant,MultiTenantTenancyDefaults— default tenant ID constant
Clock abstraction
IAppClock— injectable clock for testable time-dependent logic; defaults toSystemClockwith configurable timezone (Shanghai UTC+8 default, switchable to UTC)
Assembly reference
BitzOrcas.Domain ├── Contracts/ → IAuditableEntity, ISoftDelete, ITenantEntity, IConcurrencyTracked ├── Entities/ → `Entity<TId>`, `AggregateRoot<TId>`, DomainEvent, IDomainEvent ├── Results/ → `Result<T>`, Result, Error, ErrorType, IResult ├── Tenancy/ → TenantId, TenancyMode, TenancyDefaults ├── ValueObjects/ → ValueObject, EmailAddress, NoteTitle └── Abstractions/ → IAppClock