CloudyWing.DomainKit.EntityFrameworkCore
0.1.0
dotnet add package CloudyWing.DomainKit.EntityFrameworkCore --version 0.1.0
NuGet\Install-Package CloudyWing.DomainKit.EntityFrameworkCore -Version 0.1.0
<PackageReference Include="CloudyWing.DomainKit.EntityFrameworkCore" Version="0.1.0" />
<PackageVersion Include="CloudyWing.DomainKit.EntityFrameworkCore" Version="0.1.0" />
<PackageReference Include="CloudyWing.DomainKit.EntityFrameworkCore" />
paket add CloudyWing.DomainKit.EntityFrameworkCore --version 0.1.0
#r "nuget: CloudyWing.DomainKit.EntityFrameworkCore, 0.1.0"
#:package CloudyWing.DomainKit.EntityFrameworkCore@0.1.0
#addin nuget:?package=CloudyWing.DomainKit.EntityFrameworkCore&version=0.1.0
#tool nuget:?package=CloudyWing.DomainKit.EntityFrameworkCore&version=0.1.0
CloudyWing.DomainKit
此專案是我把我過往 Domain 架構往 DDD 演進後的結果,並讓 AI 討論並補足一些不夠熟悉的部分,作為後續研究,配合自身需求維護,可能一加功能就是破壞性變更的大改版,不特別建議其他人直接相依。
DomainKit 是一組可重用的 .NET 領域層基礎建設,提供 DDD 風格的 Entity、Aggregate Root、雙軌領域事件、審計填值、多租戶與樂觀鎖,以及 EF Core 的持久化、查詢、規約和 Outbox 基礎建設。設計前提為真 Code First。實體即 EF 對映型別並同時是富血聚合根,不使用資料物件 wrapper。
套件內容
src/DomainKit(CloudyWing.DomainKit) 核心領域基礎建設,零 EF 相依。包含Entity<TKey>、AggregateRoot<TKey>、審計與 RowVersion 標記介面、IHasExternalKey<TKey>、租戶脈絡、雙軌事件、規約、分頁與業務例外契約。src/DomainKit.EntityFrameworkCore(CloudyWing.DomainKit.EntityFrameworkCore) EF Core 持久化與基礎服務。包含 Repository、Unit of Work、QueryService、審計與並行控制攔截器、租戶 convention、Aggregate Include、Enumeration 對映、Outbox dispatcher、進程內廣播與 DI 註冊。
Installation
.NET CLI
dotnet add package CloudyWing.DomainKit
dotnet add package CloudyWing.DomainKit.EntityFrameworkCore
Quick Start
定義聚合與 DbContext
聚合根直接繼承 AggregateRootBase(鍵為 Guid),並以標記介面宣告審計欄位與狀態屬性。
using CloudyWing.DomainKit.Entities;
using CloudyWing.DomainKit.EntityFrameworkCore.Modeling;
using Microsoft.EntityFrameworkCore;
public class Customer : AggregateRootBase, IHasDisabledState,
IHasCreatedTime, IHasUpdatedTime, IHasCreator, IHasUpdater, IHasDeletedTime, IHasDeleter {
public string Name { get; private set; } = "";
public bool IsDisabled { get; private set; }
// 富血行為與審計欄位
}
public interface ICustomerDbContext {
DbSet<Customer> Customers { get; }
}
public sealed class AppDbContext : DbContext, ICustomerDbContext {
public AppDbContext(DbContextOptions<AppDbContext> options) : base(options) { }
public DbSet<Customer> Customers => Set<Customer>();
protected override void ConfigureConventions(ModelConfigurationBuilder configurationBuilder) {
base.ConfigureConventions(configurationBuilder);
configurationBuilder.ApplyDomainKitConventions(new UtcDateTimeOffsetConverter());
}
protected override void OnModelCreating(ModelBuilder modelBuilder) {
modelBuilder.Entity<Customer>(builder => {
builder.ToTable("Customers");
builder.ConfigureFullAuditedEntity();
});
}
}
註冊服務
AddDomainKit<TDbContext> 註冊 UnitOfWork、審計/事件與 RowVersion 攔截器、事件 dispatcher 及進程內廣播服務。handlerAssemblies 指定要掃描事件 handler 的組件。
services.AddDbContext<AppDbContext>(options => options.UseSqlServer(connectionString));
services.AddDomainKit<AppDbContext>(handlerAssemblies: [typeof(Program).Assembly]);
Repository 與 UnitOfWork
Repository 操作聚合,IUnitOfWork 統一提交(延後單次提交為預設)。提交時傳入的 actorId 會作為審計使用者,未傳時退回 IAuditUser.UserId。
await repository.CreateAsync(customer, cancellationToken: ct).ConfigureAwait(false);
await unitOfWork.SaveChangesAsync(actorId, ct).ConfigureAwait(false);
需要明確交易範圍時,使用 IUnitOfWork.UseTransactionAsync。
查詢與 GetFirst
GetSingleAsync 的語意是「必須剛好一筆」。當查詢需求是「依排序取第一筆」時,改用 GetFirstAsync 或 GetFirstOrDefaultAsync。
string nextName = await queryService.GetFirstAsync(
entity => entity.Name,
entity => !entity.IsDisabled,
query => query.OrderBy(entity => entity.DisplayOrder),
cancellationToken
).ConfigureAwait(false);
若查無資料但不想拋例外,改用 GetFirstOrDefaultAsync,無資料時回傳 null。
規約 Specifications
Specification<T> 只負責條件,PagedSpecification<T> 再加上排序與分頁,可直接交給 IQueryService<TEntity> 的規約多載。
ISpecification<Customer> activeCustomers = new Specification<Customer>(customer => customer.IsActive);
ISpecification<Customer> taipeiCustomers = new Specification<Customer>(customer => customer.City == "Taipei");
ISpecification<Customer> specification = activeCustomers.And(taipeiCustomers);
IPagedSpecification<Customer> pagedSpecification = PagedSpecification<Customer>.Create(
specification,
query => query.OrderBy(customer => customer.Name),
1,
20
);
PagedList<CustomerListItem> page = await queryService.GetPagedListAsync(
customer => new CustomerListItem(customer.Id, customer.Name),
pagedSpecification,
cancellationToken
).ConfigureAwait(false);
習慣以離散參數呼叫時,可透過 QueryServiceSpecificationExtensions 組成規約後委派核心多載。
審計與雙軌事件
- 聚合實作
IHasCreatedTime/IHasCreator等介面後,SaveChanges攔截器在提交時自動填入時間與操作者,不需逐操作手動指定。 - 聚合分別收集 domain event 與 integration event。Domain event 於提交前派發,handler 變更納入同一次
SaveChanges。Integration event 可與業務資料同交易寫入 Outbox,再由背景服務重試派發。
Outbox、多租戶及 provider 專屬 RowVersion 設定請依完整文件導入,避免只開啟部分元件。
版本與相容性
目標框架為 .NET 10。版號由 MinVer 依 git tag(前綴 v)推導,發版即打 tag。
0.x 期間不保證公開 API 穩定,次版號遞增即可能包含破壞性變更,變更內容記於 CHANGELOG.md。相依此套件時建議釘死明確版號,避免使用範圍或浮動版號。
公開 API 以 PublicAPI.Shipped.txt 宣告,並由 Microsoft.CodeAnalysis.PublicApiAnalyzers 於建置時檢查。未同步更新該檔的公開成員增刪會使建置失敗。
Documentation
完整文件:https://cloudywing.github.io/DomainKit/
License
本專案授權見 LICENSE.md。
| Product | Versions 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. |
-
net10.0
- CloudyWing.DomainKit (>= 0.1.0)
- CloudyWing.Enumeration (>= 0.2.0)
- Microsoft.EntityFrameworkCore (>= 10.0.8)
- Microsoft.EntityFrameworkCore.Relational (>= 10.0.8)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 10.0.8)
- Microsoft.Extensions.Hosting.Abstractions (>= 10.0.8)
- Microsoft.Extensions.Localization.Abstractions (>= 10.0.8)
- Microsoft.Extensions.Logging.Abstractions (>= 10.0.8)
- Microsoft.Extensions.Options (>= 10.0.8)
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 | 107 | 8/18/2026 |