Skip to content
bitzorcas
EN

Reference

Core building block

Foundational domain primitives — Entity\<TId\>, AggregateRoot\<TId\>, Result\<T\>, DomainEvent, value objects, tenant abstractions, and the entire type hierarchy every module depends on.

Last updated

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; carries Id (with Yitter snowflake generation by default), a private domain-event list surfaced as DomainEvents, and protected methods AddDomainEvent() / ClearDomainEvents(). Every persisted entity inherits from this.
  • AggregateRoot<TId> — extends Entity<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

  • ITenantEntityTenantId TenantId { get; }. Implemented by TenantEntityBase; marks entities as tenant-scoped. SqlSugar’s global query filter uses this to enforce multi-tenancy.
  • IAuditableEntityCreatedAt, 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:

TypePurpose
Result<T>Wraps a success value or an Error
ErrorStructured error with ErrorType, code, and description
ErrorTypeEnum: Validation, NotFound, Conflict, Unauthorized, Forbidden, Failure, Infrastructure
// Domain service returns Result instead of throwing
public 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 constraint
  • EmailAddress — validated email format
  • ValueObject — base class with equality-by-value semantics

Tenant abstractions

  • TenantId — strongly-typed tenant identifier (wraps string)
  • TenancyMode — enum: SingleTenant, MultiTenant
  • TenancyDefaults — default tenant ID constant

Clock abstraction

  • IAppClock — injectable clock for testable time-dependent logic; defaults to SystemClock with 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