Asgard 插件项目结构 skill。Use when scaffolding a new Asgard plugin project, defining folder layout, choosing starter files, separating plugin implementation from starter/host bootstrap, placing DTO/VO/Entity/Repository/Service code, configuring GlobalUsings.cs, or deciding where Mapper, Config, app.yaml, and plugin.yaml belong.
这个 skill 是 Asgard 插件项目结构的权威来源。
它负责定义:
Program.cs、plugin.yaml、app.yaml 各自默认归属GlobalUsings.cs、ProjectReference、基础依赖怎么放DTO / VO / Entity / Repository / Service / Mapper 应该放在哪它不负责讲某一层的具体实现细节。
具体写法请继续引用:
$asgard-api-development$asgard-database$asgard-repository-service-registration$asgard-configuration$asgard-plugin-lifecycle$asgard-dotnet-10-csharp-14在以下场景必须优先使用本 skill:
GlobalUsings.cs、Program.cs、app.yaml、plugin.yaml 放置位置Mapper、Models、Domains、Services 的归属DTO / VO / Entity 的分层与流转Asgard 当前认可、也更希望推广的形式是:
Asgard.Heimdall 这类项目负责插件主体实现Asgard.Heimdall.Starter 这类项目负责启动入口与调试承载可以接受两种模式:
适用场景:
特点:
Program.cs、plugin.yaml、业务代码位于同一项目app.yaml 可以直接放在同项目根目录适用场景:
特点:
Program.cs、启动参数、宿主编排、调试入口ProjectReference 引用插件主体项目PluginWebAppDefaults.RunAsync<TPlugin>() 通常位于 starter,而不是业务插件主体项目<ItemGroup>
<PackageReference Include="Asgard.Analyzers" />
<PackageReference Include="Asgard.PluginSdk" />
</ItemGroup>
app.yaml 的加载路径Program.cs
默认属于 starter / host / 启动项目,不要默认放进插件主体项目plugin.yaml
默认属于插件主体项目,是插件清单与插件级元数据app.yaml
属于运行配置入口,是否与插件项目同目录取决于组织方式GlobalUsings.cs
插件项目与 starter 项目都可以有,但各自只维护自己的全局 using{ProjectName}/
├── app.yaml
├── plugin.yaml
├── GlobalUsings.cs
├── Program.cs
├── {ProjectName}.csproj
├── Config/
│ ├── PluginConfigs/
│ │ └── {PluginConfigClassName}.cs
│ └── {ThirdPartyName}/
│ └── {ThirdPartyConfigClassName}.cs
├── wwwroot/
│ └── {StaticAssetFiles}
├── Controllers/
│ └── {FeatureName}Controller.cs
├── Mapper/
│ └── {AggregateName}Mapper.cs
├── Models/
│ ├── VO/
│ │ └── {AggregateName}Vo.cs
│ ├── DTO/
│ │ └── {AggregateName}Dto.cs
│ └── Entities/
│ └── {AggregateName}Entity.cs
├── Domains/
│ ├── IRepositories/
│ │ └── I{AggregateName}Repository.cs
│ └── Repositories/
│ └── {AggregateName}Repository.cs
├── Services/
│ ├── IServices/
│ │ └── I{AggregateName}Service.cs
│ └── Services/
│ └── {AggregateName}Service.cs
├── Extensions/
│ └── {FeatureName}Extensions.cs
├── Middlewares/
│ └── {FeatureName}Middleware.cs
└── {CustomModuleName}/
└── {CustomFiles}
{SolutionRoot}/
├── src/
│ ├── {PluginProjectName}/
│ │ ├── plugin.yaml
│ │ ├── GlobalUsings.cs
│ │ ├── {PluginProjectName}.csproj
│ │ ├── {PluginClassName}.cs
│ │ ├── Config/
│ │ │ ├── PluginConfigs/
│ │ │ │ └── {PluginConfigClassName}.cs
│ │ │ └── {ThirdPartyName}/
│ │ │ └── {ThirdPartyConfigClassName}.cs
│ │ ├── wwwroot/
│ │ │ └── {StaticAssetFiles}
│ │ ├── Controllers/
│ │ │ └── {FeatureName}Controller.cs
│ │ ├── Mapper/
│ │ │ └── {AggregateName}Mapper.cs
│ │ ├── Models/
│ │ │ ├── VO/
│ │ │ │ └── {AggregateName}Vo.cs
│ │ │ ├── DTO/
│ │ │ │ └── {AggregateName}Dto.cs
│ │ │ └── Entities/
│ │ │ └── {AggregateName}Entity.cs
│ │ ├── Domains/
│ │ │ ├── IRepositories/
│ │ │ │ └── I{AggregateName}Repository.cs
│ │ │ └── Repositories/
│ │ │ └── {AggregateName}Repository.cs
│ │ ├── Services/
│ │ │ ├── IServices/
│ │ │ │ └── I{AggregateName}Service.cs
│ │ │ └── Services/
│ │ │ └── {AggregateName}Service.cs
│ │ ├── Extensions/
│ │ │ └── {FeatureName}Extensions.cs
│ │ ├── Middlewares/
│ │ │ └── {FeatureName}Middleware.cs
│ │ └── {CustomModuleName}/
│ │ └── {CustomFiles}
│ └── {StarterProjectName}/
│ ├── app.yaml
│ ├── GlobalUsings.cs
│ ├── Program.cs
│ └── {StarterProjectName}.csproj
└── {SolutionName}.slnx
{PluginClassName}.cs
插件入口类,继承 PluginBaseConfig/
放配置类、配置绑定辅助类型、第三方集成配置相关代码Config/PluginConfigs/
放 Asgard 插件自身配置类Config/{ThirdPartyName}/
放第三方组件或外部系统配置类wwwroot/
放静态资源Controllers/
放所有 API 声明;Controller 只负责输入输出编排,把 Service 返回的 DTO 转成 VO 后,再统一包装成 Response<T> / PageResponse<T> / CursorResponse<T> 对外返回Mapper/
放对象映射器Models/VO/
放对外展示模型Models/DTO/
放数据传输模型Models/Entities/
放数据库实体模型Domains/IRepositories/
放仓储接口Domains/Repositories/
放仓储实现Services/IServices/
放服务接口Services/Services/
放服务实现Extensions/
放扩展方法和扩展装配Middlewares/
放自定义中间件{CustomModuleName}/
这是“其他自定义目录”的占位写法,不是必须创建名为 yyy 的真实目录plugin.yaml
放插件清单与插件元数据Program.cs
启动入口、调试入口、参数解析入口GlobalUsings.cs
只维护启动项目自身需要的全局 usingProjectReference
引用插件主体项目app.yaml
运行配置入口,决定宿主启动期加载的主配置PluginWebAppDefaults.RunAsync<TPlugin>()
快速验证或单插件启动的默认位置YggdrasilHost.CreateBuilder(...)
宿主构建、插件装配、中间件编排、启动路径选择Program.cs、app.yaml、plugin.yaml 一刀切地说成“插件项目根目录标配”Program.cs 默认属于 starter;只有在模式 A 快速验证时,才与插件实现同项目plugin.yaml 默认属于插件主体项目app.yaml 默认由启动承载方加载app.yaml 作为输出资源,但必须明确说明由 starter / host 加载,或复制到运行目录后再加载输出链路固定为:
数据库
↓
Entity 【查询】
↓
Service → 转 DTO 【业务处理】
↓
Controller → 转 VO 【给前端展示】
↓
前端页面
实际代码访问时,Service 仍然必须通过 Repository 访问 Entity 与数据库。上面的输出链路强调的是模型转换职责,不表示可以跳过仓储层。
补充强制要求:
Controller 层对外输出时,必须把结果包装成 Asgard 统一响应模型Service 层内部返回仍然遵循 DTO 边界,不直接承担统一响应壳职责Response<T> / Response<object>PageResponse<T>CursorResponse<T>Controllers/ 中直接返回裸 VO、裸集合或自定义另一套响应壳进入链路按反方向理解:
前端请求
↓
Controller
↓
Service
↓
Repository
↓
Entity
↓
数据库
Mapper/ 默认一类一映射器Mapper/ 中补显式配置Asgard.PluginSdk 已经带上常用映射能力,默认先用框架已有能力,不要另起一套Asgard.PluginSdk 提供的 PluginObjectMapperPluginObjectMapper 默认支持 class -> class 同名同类型(或类型兼容)属性映射,不要求固定继承基类PluginObjectMapper.Map<TSource, TDestination>(source)PluginObjectMapper.MapToExisting<TSource, TDestination>(source, destination)PluginObjectMapper.MapList<TSource, TDestination>(source)$asgard-dotnet-10-csharp-14references/project-layout.mdreferences/layer-flow.mdreferences/package-globalusings-and-mapper.mdtemplates/AsgardPlugin.csproj.templatetemplates/AsgardStarter.csproj.templatetemplates/GlobalUsings.cs.templatetemplates/Program.cs.templatetemplates/app.yaml.templatetemplates/plugin.yaml.templatetemplates/project-tree.txt.templateexamples/minimal-plugin-structure.mdexamples/full-plugin-structure.md