资讯动态

Webiny API 架构模式:从 `createFeature` 到 Service/UseCase 纵向切片的依赖注入与代码组织规范

发布时间:2026/10/10 7:41:59 来源:尧图企业网站定制
CMS后端前端【免费下载链接】webiny-jsOpen-source, self-hosted CMS platform on AWS serverless (Lambda, DynamoDB, S3). TypeScript framework with multi-tenancy, lifecycle hooks, GraphQL API, and AI-assisted development via MCP server. Built for developers at large organizations.项目地址https://gitcode.com/gh_mirrors/we/webiny-js点击查看免费下载导读本文是 Webiny 仓库中 api-architect 技能文档 的完整展开版。Webiny 是一套运行在 AWS Serverless 之上的开源、可自托管 CMS 平台Lambda、DynamoDB、S3其 API 后端以Feature纵向切片为组织单元以createFeaturecreateAbstraction为骨架构建依赖注入体系。读完本文你将掌握 Webiny 中 Service多方法单例与 UseCase单方法瞬态编排器的取舍、Feature 命名与目录模板、DI 决策树、容器注册方法、领域错误与实体值对象模式以及扩展开发者extensions/与核心开发者packages/两种写法的差异可直接用于编写可维护、可测试、符合 Webiny 官方代码约定的后端功能。该技能文档面向任何 Webiny 后端 API 开发工作是仓库内 API 架构相关子技能UseCase 模式、权限、事件处理、自定义 GraphQL 等的总入口。文中所有代码示例均为 TypeScript并可参考仓库中 packages/feature 与 extensions/ 下的真实实现加以印证。一、适用范围两种开发模式同一套架构本文同时适用于两类开发者架构模式完全一致只有导入来源与注册方式不同对比项扩展开发者extensions/核心开发者packages/导入来源webiny/api、webiny/api/cms/model等webiny/feature/api、webiny/api-headless-cms/...等目录清单路径使用Import:路径使用Source:路径入口点export default createFeature(...)由Api.Extension src{...}指向的文件导出createFeature由包初始化器package initializer注册GraphQL 模式export default GraphQLSchemaFactory.createImplementation(...)在入口点的createFeature内通过container.register()注册同一模式但从webiny/handler-graphql导入判断当前所在上下文只需看文件路径在extensions/下即为扩展模式在packages/下即为核心模式。两种模式最终都收敛到同一个createFeatureAPI——这正是该仓库刻意保持的一致抽象。TL;DRAPI 扩展使用createFeature向 DI 容器注册功能每个 Feature 是一个纵向切片包含抽象、实现与一个feature.ts注册文件核心抽象是Service多方法、单例与UseCase单方法编排器、瞬态Repository 通过 CMS 完成持久化Feature 按业务能力命名其内部文件按技术职责命名。二、架构总览从 Extension 到 Repository 的分层链路Extension (root) ── registers ── Features GraphQL Schemas Models Feature ── registers ── UseCase | Service | EventHandler Repository UseCase ── depends on ── Service | Repository ( EventPublisher) Repository ── depends on ── CMS Use Cases (GetModel, CreateEntry, etc.) Service ── depends on ── external APIs, other Services各层职责Extension顶层入口注册所有 Feature、GraphQL Schema 与 CMS Model。Feature纵向切片vertical slice注册自己的 UseCase、Service、Repository 与事件处理器。UseCase单方法编排器execute()协调 Service、Repository 与领域事件瞬态作用域。Service面向外部 API 调用或内聚领域逻辑的多方法抽象单例作用域。Repository以 CMS 为存储的持久化层单例作用域。EventHandler响应领域事件的薄编排器委托给 Service/UseCase。GraphQL Schema定义类型、输入、查询与变更resolver 委托给 UseCase。CMS Model定义存入 Headless CMS 的数据结构。从仓库源码看这一层级的基石位于 packages/feature 包createAbstraction.ts 中createAbstractionT(name)仅一行return new AbstractionT(name)即基于webiny/di的Abstraction创建一个带类型的 DI 令牌token。api/createFeature.ts 中createFeature(def)返回{ name, register }并通过Reflect.defineMetadata(wby:isFeature, true, feature)打上“这是一个 Feature”的元数据标记供框架按扩展加载。api/index.ts 统一对外导出Container、createDecorator、createImplementation、createFeature、createAbstraction、Result、ResultAsync、BaseError——这正是文档中import { createFeature } from webiny/api的背后实现。三、Services vs UseCases何时用哪种抽象Service多方法抽象Service 用于外部 API 调用或内聚的领域逻辑把彼此相关的操作聚合在一个对象上。// abstractions.ts export interface ILingotekService { translate(documentId: string, targetLocale: string): PromiseResultvoid, Error; getTranslationStatus(documentId: string): PromiseResultTranslationStatus, Error; deleteProject(projectId: string): PromiseResultvoid, Error; } export const LingotekService createAbstractionILingotekService(MyExt/LingotekService); export namespace LingotekService { export type Interface ILingotekService; }要点以单例作用域注册.inSingletonScope()。位于features/{serviceName}/或features/services/{serviceName}/。一个外部系统或一个内聚领域对应一个 Service。若需要异步引导async bootstrap——例如从 CMS 加载设置、拉取远端配置或 API 令牌——应改用ServiceProvider 模式见下文由提供者抽象暴露async getService()惰性初始化并缓存 Service。消费者注入 Provider 而非 Service 本身。UseCase单方法编排器UseCase 只有一个execute()方法负责协调 Service、Repository 与事件。export interface ISyncProjectUseCase { execute(input: SyncProjectInput): PromiseResultProject, SyncProjectError; }要点以瞬态作用域注册默认即不显式调用.inSingletonScope()。位于features/{ActionEntity}/。一个业务操作对应一个 UseCase。什么时候应该创建 UseCaseGraphQL mutation 与事件处理器需要复用同一段逻辑需要协调多个 Service 或多个 Repository业务逻辑必须在多个入口GraphQL、事件、CLI间复用。什么时候不该创建 UseCase简单事件处理器只调用一个 Service 方法——直接注入 Service简单读查询——直接在 GraphQL resolver 中注入 Service 或 Repository逻辑只存在于一个地方且几乎不可能复用。ServiceProvider 模式异步引导// abstractions.ts export interface ILingotekServiceProvider { getService(): PromiseILingotekService; } export const LingotekServiceProvider createAbstractionILingotekServiceProvider( MyExt/LingotekServiceProvider ); export namespace LingotekServiceProvider { export type Interface ILingotekServiceProvider; }// LingotekServiceProvider.ts class LingotekServiceProviderImpl implements ProviderAbstraction.Interface { private service: ILingotekService | undefined; constructor(private getSettings: GetSettingsUseCase.Interface) {} async getService(): PromiseILingotekService { if (!this.service) { const result await this.getSettings.execute(); const settings result.isOk() ? result.value : defaultSettings; this.service new LingotekService(settings); } return this.service; } }规则Provider 以单例注册因为它缓存 Service 实例Service 本身不注册进 DI由 Provider 创建消费方在每次使用前调用await provider.getService()UseCase 与 Handler 注入LingotekServiceProvider而非LingotekService。四、Feature 命名哲学两级命名约定Feature 目录采用两级命名Feature 目录名 业务能力它为业务做了什么目录内文件名 技术职责每个文件处理什么。这样 Feature 按“它们做什么”被发现进入目录后又能立刻看清技术组件。正确示例features/ ├── syncToLingotek/ ← 业务能力 │ ├── abstractions.ts │ ├── SyncProjectUseCase.ts ← 技术职责 │ ├── EntryAfterCreateHandler.ts ← 技术职责作为文件名完全没问题 │ ├── EntryAfterUpdateHandler.ts │ └── feature.ts ├── cleanupLingotekDocument/ │ ├── EntryBeforeDeleteHandler.ts │ └── feature.ts错误示例features/ ├── EntryAfterCreateHandler/ ← ❌ 技术名当 Feature 目录名 ├── DocumentBeforeDeleteHandler/ ← ❌ 技术名当 Feature 目录名规则Feature 目录描述业务能力syncToLingotek、cleanupOnDelete、notifySlack目录内文件描述技术职责EntryAfterCreateHandler.ts、SyncProjectUseCase.ts事件处理器本身就是 Feature——它们住在features/下绝不放单独的handlers/目录。仓库中的真实例子可参考 extensions/bulkActions/applyDiscount/admin/Extension.tsxFeature 命名为BulkActions/ApplyDiscount符合AppName/FeatureName约定其register(container)里注册了DiscountAppliedEventHandler而该处理器文件与其 Feature 位于同一目录。五、Feature 结构模板简单事件处理器 Feature适用处理器只调用一个 Service 或 UseCase无需新增抽象。features/cleanupOnDelete/ ├── CleanupOnDeleteHandler.ts # 实现已有的 EventHandler 抽象 └── feature.ts # 注册该处理器含 UseCase 的复杂 Feature适用逻辑被 GraphQL 与事件处理器复用或需要协调多个 Service。features/syncProjectToLingotek/ ├── abstractions.ts # 本 Feature 的 UseCase 错误类型 ├── CreateProjectUseCase.ts ├── UpdateProjectUseCase.ts ├── DeleteProjectUseCase.ts ├── EntryAfterCreateHandler.ts # 薄处理器 → 委托给 CreateProjectUseCase ├── EntryAfterUpdateHandler.ts # 薄处理器 → 委托给 UpdateProjectUseCase ├── EntryAfterDeleteHandler.ts # 薄处理器 → 委托给 DeleteProjectUseCase └── feature.ts # 注册一切Service Feature适用面向外部 API 或领域区的可复用多方法服务。features/lingotekService/ ├── abstractions.ts # Service 接口多方法 ├── LingotekService.ts # 实现 └── feature.ts # 以单例作用域注册六、DI 决策树该注入什么正在构建…它需要…注入事件处理器调用外部 APIService事件处理器协调 CMS 外部系统UseCase事件处理器仅日志/校验Logger或什么都不注入GraphQL Resolver简单读取直接注入 Service 或 RepositoryGraphQL Resolver复杂变更UseCaseGraphQL Resolver检查权限IdentityContext 或 Permissions 抽象UseCase调用外部 APIServiceUseCase持久化/读取数据RepositoryUseCase发布领域事件EventPublisherUseCase检查权限IdentityContext 或 Permissions 抽象Repository访问 CMSGetModelUseCase、CreateEntryUseCase 等这张表是日常开发中最常查的“路由表”它把“我的代码要做哪类事”直接映射到“该依赖哪个抽象”避免业务逻辑被塞进错误的分层。七、反模式Anti-Patterns❌ 为每个操作创建独立抽象而不是一个多方法 Service// WRONG — 相关操作各自拆成独立抽象 export const DeleteDocumentService createAbstraction(...) export const CreateDocumentService createAbstraction(...) export const UpdateDocumentService createAbstraction(...) // CORRECT — 一个多方法 Service export interface IDocumentService { create(input: CreateInput): PromiseResultDoc, Error; update(id: string, input: UpdateInput): PromiseResultDoc, Error; delete(id: string): PromiseResultvoid, Error; } export const DocumentService createAbstractionIDocumentService(MyExt/DocumentService);❌ 用技术实现命名 Featurefeatures/DocumentBeforeDeleteHandler/ ← WRONG: 技术名 features/cleanupLingotekDocument/ ← CORRECT: 业务能力❌ 假设工厂存在 builder 模式// WRONG — 仓库中不存在 builder 模式 builder.role({ ... }).permissions([...]) // CORRECT — 工厂返回普通对象 async execute(): PromiseCodeRole[] { return [{ name: Admin, slug: admin, description: ..., permissions: [...] }]; }❌ 单独的 handlers/ 目录api/handlers/MyHandler.ts ← WRONG: 处理器是 Feature features/myFeature/MyHandler.ts ← CORRECT: 处理器住在自己的 Feature 里❌ 用泛型 Error 而非领域错误// WRONG throw new Error(Not found); // CORRECT return Result.fail(new EntityNotFoundError(id));❌ 事件处理器不按模型/实体类型过滤// WRONG — 对所有模型都触发 async handle(event) { await this.service.doWork(event.payload.entry); } // CORRECT — 按自己的模型过滤 async handle(event) { if (event.payload.model.modelId ! MY_MODEL_ID) return; await this.service.doWork(event.payload.entry); }八、API 目录结构总览api/ ├── Extension.ts # API 入口createFeature注册一切 ├── domain/ │ ├── errors.ts # 领域错误继承 BaseError │ ├── EntityId.ts # 实体 ID 值对象 │ ├── EntityModel.ts # CMS 模型定义ModelFactory │ └── EntityModelExtension.ts # 用于扩展模型的抽象 ├── features/ │ ├── createEntity/ # Feature: 业务能力 │ │ ├── abstractions.ts # UseCase Repository 抽象 错误类型 │ │ ├── feature.ts # DI 注册 │ │ ├── CreateEntityUseCase.ts │ │ └── CreateEntityRepository.ts │ ├── lingotekService/ # Service Feature │ │ ├── abstractions.ts │ │ ├── LingotekService.ts │ │ └── feature.ts │ └── syncToLingotek/ # 事件处理器 Feature │ ├── EntryAfterCreateHandler.ts │ └── feature.ts └── graphql/ ├── CreateEntitySchema.ts └── GetEntitySchema.ts九、API 扩展入口点Extension.ts// src/api/Extension.ts import { createFeature } from webiny/api; import EntityModel from ./domain/EntityModel.js; import CreateEntitySchema from ./graphql/CreateEntitySchema.js; import { CreateEntityFeature } from ./features/createEntity/feature.js; import { LingotekServiceFeature } from ./features/lingotekService/feature.js; import { SyncToLingotekFeature } from ./features/syncToLingotek/feature.js; export const Extension createFeature({ name: MyExtension, register(container) { // CMS model先注册 container.register(EntityModel); // GraphQL schemas container.register(CreateEntitySchema); // Features用 Feature.register不要用 container.register CreateEntityFeature.register(container); LingotekServiceFeature.register(container); SyncToLingotekFeature.register(container); } });注册规则先注册 CMS ModelGraphQL Schema 用container.register()注册Feature 用Feature.register(container)注册不是container.register(Feature)。在 api/createFeature.ts 中可以看到createFeature返回的就是{ name, register }结构因此Feature.register(container)与container.register(...)的区别在于Feature 对象本身不当作 DI 依赖注册而是把它的register回调用于向容器注册其内部组件。十、Abstractions 与 Feature 注册抽象定义Abstractions每段业务逻辑都从带类型的抽象令牌开始// src/api/features/createEntity/abstractions.ts import { createAbstraction, Result } from webiny/api; import type { MyEntity } from ~/shared/MyEntity.js; export interface ICreateEntityInput { name: string; } export interface ICreateEntityUseCase { execute(input: ICreateEntityInput): PromiseResultMyEntity, Error; } export const CreateEntityUseCase createAbstractionICreateEntityUseCase( MyExtension/CreateEntityUseCase ); // 命名空间重导出所有相关类型方便消费方访问 export namespace CreateEntityUseCase { export type Interface ICreateEntityUseCase; export type Input ICreateEntityInput; }注意第 4 行使用了~别名指向包内绝对导入ESM 下的代码约定之一。Feature 注册feature.ts// src/api/features/createEntity/feature.ts import { createFeature } from webiny/api; import CreateEntityUseCase from ./CreateEntityUseCase.js; import CreateEntityRepository from ./CreateEntityRepository.js; export const CreateEntityFeature createFeature({ name: CreateEntity, register(container) { container.register(CreateEntityUseCase); // 瞬态默认 container.register(CreateEntityRepository).inSingletonScope(); // 单例 } });容器注册方法一览方法适用场景container.register(Implementation)注册一个类通过Abstraction.createImplementation创建container.registerInstance(abstraction, instance)注册一个满足接口的普通对象container.registerFactory(abstraction, () instance)注册惰性工厂container.registerDecorator(Decorator)注册装饰器包装既有实现十一、通过 BuildParams 读取配置禁止运行期读 process.env已部署的 API绝不能在运行期用process.env读取配置。所有配置经由 DI 通过BuildParams流入import { BuildParams } from webiny/api; class MyServiceImpl implements MyService.Interface { constructor(private buildParams: BuildParams.Interface) {} doSomething() { // buildParams.get() 返回 T | null —— 始终处理 null const endpoint this.buildParams.getstring(MY_API_ENDPOINT); if (!endpoint) { throw new Error(MY_API_ENDPOINT build param is not configured.); } } } export default MyService.createImplementation({ implementation: MyServiceImpl, dependencies: [BuildParams] });注意BuildParam的声明Api.BuildParam位于顶层扩展组件中——详见webiny-full-stack-architect技能。仓库中packages/api-event-handler-aws-ddb/__tests__/featureFlagsBuildParam.test.ts即是对 BuildParam 特性的测试佐证。十二、领域错误Domain Errors每个 Feature 定义继承BaseError的领域专属错误// domain/errors.ts import { BaseError } from webiny/api; export class EntityNotFoundError extends BaseError { override readonly code Entity/NotFound as const; constructor(id: string) { super({ message: Entity with id ${id} was not found! }); } } export class EntityPersistenceError extends BaseError{ error: Error } { override readonly code Entity/Persist as const; constructor(error: Error) { super({ message: error.message, data: { error } }); } }规则继承webiny/api导出的BaseError使用override readonly code值为带命名空间的字符串Domain/ErrorType在 code 上使用as const以获得类型收窄若要传递data先定义类型再作为泛型传入BaseErrorTDataType。从源码看api/BaseError.ts 中BaseErrorTData void是一个抽象类code为必填的抽象只读属性data在TData为void时为undefined构造函数通过ErrorDataWithOptionalDataTData区分是否携带data字段。抽象中的类型化错误联合定义错误接口与联合类型让消费方精确知道可能发生哪些错误// features/createEntity/abstractions.ts export interface ICreateEntityErrors { persistence: EntityPersistenceError; notFound: EntityModelNotFoundError; notAuthorized: NotAuthorizedError; } type CreateEntityError ICreateEntityErrors[keyof ICreateEntityErrors]; export interface ICreateEntityUseCase { execute(input: CreateEntityInput): PromiseResultEntity, CreateEntityError; } export namespace CreateEntityUseCase { export type Interface ICreateEntityUseCase; export type Input CreateEntityInput; export type Error CreateEntityError; export type Return PromiseResultEntity, CreateEntityError; }UseCase 的错误是 Repository 错误的超集UseCase 还叠加了授权、校验等错误在命名空间中导出Error与Return类型供消费方使用。Result的实现位于 api/Result.tsResult.ok()与Result.fail()构造成功/失败值isOk()/isFail()是类型守卫this is { _value: TValue } ResultTValue, TError配合value/errorgetter 在失败时抛出明确错误map、mapError、flatMap、match提供函数式组合与模式匹配能力Result.UnwrapResultT/Result.UnwrapErrorT可从返回值中提取 Ok/Err 类型。十三、实体与值对象模式实体 ID 值对象// domain/EntityId.ts import { EntryId } from webiny/api/cms/entry; export class EntityId { static from(id?: string) { if (id) { return EntryId.from(id).id; // 确保不带修订号后缀的干净 id } return EntryId.create().id; } }领域实体类// shared/Entity.ts export interface EntityDto { id: string; values: EntityValues; } export class Entity { private constructor(private dto: EntityDto) {} static from(dto: EntityDto) { return new Entity(dto); } get id() { return this.dto.id; } get values() { return this.dto.values; } }模式要点实体通过私有构造函数 静态工厂from创建ID 经由EntryIdCMS entry 的 ID 值对象保证规范化。十四、公共导出index.ts只导出抽象每个 Feature 文件夹的index.ts只导出抽象——绝不导出 Feature、事件或实现// features/disableEntity/index.ts export { DisableEntityUseCase, EntityBeforeDisableEventHandler, EntityAfterDisableEventHandler } from ./abstractions.js;规则使用export { }语法不是export *不导出feature.ts、events.ts或实现文件。十五、作用域规则Scoping层作用域理由UseCase瞬态默认每次调用全新实例Service.inSingletonScope()有状态或创建成本高Repository.inSingletonScope()单一缓存实例Gateway.inSingletonScope()无状态但创建成本高EventHandler瞬态默认每个事件全新实例CMS Model正常注册启动时注册一次GraphQL Schema正常注册启动时注册一次十六、命名约定工件模式示例Feature 目录{businessCapability}camelCasesyncToLingotek、createEntityUseCase{Action}{Entity}UseCaseCreateTenantUseCaseService{Domain}ServiceLingotekServiceRepository{Action}{Entity}RepositoryCreateTenantRepositoryEvent{Entity}{Before\|After}{Action}EventTenantBeforeDisableEventHandler{Entity}{Before\|After}{Action}EventHandlerTenantBeforeDisableEventHandlerDecorator{Action}{Entity}With{Concern}GetEntityByIdWithAuthorizationMapperEntryTo{Entity}MapperEntryToFolderMapperError{Entity}{Problem}ErrorEntityNotFoundError十七、代码约定Code Conventions使用webiny/api的createAbstraction——绝不new Abstraction()所有实现使用createImplementation其dependencies数组与构造函数参数顺序一一对应实现类不导出——只导出createImplementation的结果作为default每文件一个类每行一个具名导入所有相对导入使用.js扩展名ESM包内绝对导入使用~别名所有操作返回ResultT, E先检查result.isFail()再访问result.value绝不返回null——使用领域专属NotFoundError将基础设施错误包装为领域错误。十八、构建新 API Feature 的检查清单领域错误继承BaseError并使用override readonly code抽象定义错误接口、联合类型与包含InterfaceError的命名空间UseCase 实现抽象.Interface使用createImplementationRepository 实现抽象.Interface使用 CMS UseCase包装错误Feature 注册 UseCase瞬态与 Repository单例装饰器用container.registerDecorator()注册被装饰者位于构造参数最后一位根 Extension 注册 Model、Schema 与 FeaturesGraphQL Schema 实现GraphQLSchemaFactory.Interface领域事件具有带InterfaceEvent命名空间的处理器抽象index.ts只导出抽象——不含 Feature、事件类或实现所有相对导入使用.js扩展名每文件一个类每行一个导入十九、核心 API 速查createAbstractionT(name: string)创建带类型的 DI 令牌泛型T是实现必须满足的接口。导入import { createAbstraction } from webiny/api返回AbstractionT源码实现见 packages/feature/src/createAbstraction.ts本质是webiny/di中AbstractionT的一行包装。createFeature(def)创建被框架作为扩展加载的 Feature 定义。导入import { createFeature } from webiny/apidef.name唯一的 Feature 名约定AppName/FeatureNamedef.register(container)启动时以 DIContainer实例调用源码实现见 packages/feature/src/api/createFeature.tscreateFeature支持TRegister泛型以表达register回调是否接收 context 参数并给返回对象打上wby:isFeature元数据。二十、关键规则Key Rules抽象优先——任何新业务逻辑都必须封装进createAbstractioncreateFeature绝不直接把逻辑写进 EventHandler、GraphQL resolver 或 CLI 命令命名空间约定——每个抽象导出namespace MyAbstraction { export type Interface ...; }让消费方用MyAbstraction.Interface标注依赖类型名称唯一性——Feature 名必须全局唯一使用AppName/FeatureName约定构造参数顺序——dependencies数组必须与构造函数参数顺序完全一致运行期无process.env——已部署的 API Service 绝不读process.env一切配置经BuildParams流入作用域——UseCase 瞬态默认Service/Repository 单例.inSingletonScope()导入扩展名——ESM 下导入路径始终使用.js扩展名。这些规则的载体均可在 packages/feature/src/api/index.ts 的导出面中找到对应实现且extensions/目录下的真实扩展如 bulkActions/applyDiscount/admin/Extension.tsx正是按此约定书写的范例。二十一、相关技能导航本技能是 Webiny API/后端架构的枢纽技能深层实现细节由以下子技能承接均见仓库skills/user-skills/下对应文档webiny-use-case-pattern—— UseCase 实现、Result 处理、错误类型、装饰器、CMS Repositorywebiny-api-permissions—— 基于 Schema 的权限、CRUD 授权模式、own-record 作用域、测试webiny-event-handler-pattern—— EventHandler 生命周期、领域事件定义与发布、处理器抽象webiny-custom-graphql-api—— GraphQL Schema 创建、动态输入、带命名空间的 mutationwebiny-http-route—— 通过Api.Route与HttpRouteHandler.Interface定制 HTTP 端点webiny-v5-to-v6-migration—— 面向 AI Agent 的并排迁移模式webiny-full-stack-architect—— 顶层组件、共享领域层、BuildParam 声明webiny-dependency-injection——createImplementationDI 模式与可注入服务。在动手编写任何后端 API 功能前先对照本文的检查清单与命名表自查再按需查阅上述子技能即可保证产出符合 Webiny 官方的分层、命名与 DI 约定。赞分享CMS后端前端【免费下载链接】webiny-jsOpen-source, self-hosted CMS platform on AWS serverless (Lambda, DynamoDB, S3). TypeScript framework with multi-tenancy, lifecycle hooks, GraphQL API, and AI-assisted development via MCP server. Built for developers at large organizations.项目地址https://gitcode.com/gh_mirrors/we/webiny-js点击查看免费下载相关推荐Cal.com 按领域组织的垂直切片架构以 packages/features 为心脏的代码组织规范Cal.com 按领域组织的垂直切片架构以 packages/features 为心脏的代码组织规范 垂直切片架构Vertical Slice Archit后端前端企业应用Langflow 架构边界与单向依赖规范代码落点决策树、API 变更协议与 Service 设计指南Langflow 架构边界与单向依赖规范代码落点决策树、API 变更协议与 Service 设计指南 Langflow 不是一个单一的应用而是由可运行的人工智能大模型AI AgentRAG后端前端MCP 服务工作流自动化MobileNetV4 小型卷积模型安全与隐私考虑移动端AI的挑战与解决方案MobileNetV4 小型卷积模型安全与隐私考虑移动端AI的挑战与解决方案 MobileNetV4 小型卷积模型mobilenetv4_conv_smal上一篇如何用Dism清理系统与备份镜像完成首次有效清理下一篇.NET runtime 仓库 CoreCLR 测试配置完全指南CLRTestKind、优先级与测试工程编写规范创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑