Medusa 开源电商平台开发指南:Monorepo 结构、构建命令与核心架构模式全解析
发布时间:2026/9/10 6:07:44
分类:文化教育
浏览:1234

Medusa 开源电商平台开发指南Monorepo 结构、构建命令与核心架构模式全解析【免费下载链接】medusaThe worlds most flexible commerce platform for agents and developers项目地址: https://gitcode.com/GitHub_Trending/me/medusaMedusa 是一个基于 TypeScript 的开源电商平台其代码库以包含 30 模块化商业包的 monorepo 形式组织。本文以仓库根目录的 CLAUDE.md 为骨架系统讲解 Medusa Core 的代码库结构、构建与测试命令、代码风格约定以及 Module 服务、API 路由、Workflow 编排、错误处理等核心架构模式并辅以仓库内的源码与配置文件作为可验证依据。读完本文你将能够快速定位 Medusa 各业务模块、正确执行构建与迁移命令、遵循项目规范编写符合 Medusa 架构风格的模块、路由与工作流代码。一、Medusa Core 仓库概览Medusa Core 是一个 TypeScript monorepo采用 Yarn Workspaces 管理包含 30 多个模块化的商业包commerce packages。仓库根目录的 package.json 声明了全部 workspaces覆盖packages/medusa、packages/modules/*、packages/modules/providers/*、packages/core/*、packages/cli/*、packages/admin/*、packages/design-system/*以及integration-tests/**/*。仓库的目录布局如下/packages/ ├── medusa/ # Medusa 主包API 路由、命令、加载器 ├── core/ # 核心框架包 │ ├── framework/ # 核心运行时HTTP、数据库、依赖注入 │ ├── types/ # TypeScript 类型定义 │ ├── utils/ # 工具函数与装饰器 │ ├── workflows-sdk/ # 工作流组合 SDK │ ├── core-flows/ # 预定义工作流 │ └── modules-sdk/ # 模块开发 SDK ├── modules/ # 30 商业模块 │ ├── product/, order/, cart/, payment/... │ └── providers/ # 15 提供商实现payment-stripe、file-s3 等 ├── admin/ # 管理后台包 │ └── dashboard/ # React 管理后台 UI ├── cli/ # CLI 工具 └── design-system/ # UI 组件与图标库 /integration-tests/ # 全栈集成测试 /www/ # 文档站点其中四个关键目录需要特别关注packages/core/framework —— 核心运行时负责 HTTP 层、数据库层与依赖注入容器packages/medusa/src/api —— API 路由实现采用文件系统路由约定packages/modules —— 各商业功能模块如 order、product、cart、payment 等packages/admin/dashboard —— 基于 React 的管理后台应用。提示当处理www/apps/api-reference下的 API 参考文档时应先阅读 www/apps/api-reference/CLAUDE.md 了解其路径结构与 OAS 到公开文档的转换流程当处理www/apps/resources下的资源文档时应先阅读 www/apps/resources/CLAUDE.md 了解参考文档的生成与构建方式。二、构建系统与常用命令2.1 包管理器与构建Medusa 使用Yarn 3.2.1作为包管理器linker 模式为node-modules见根 package.json 中packageManager: yarn3.2.1字段。常用命令如下# 安装全部依赖 yarn install # 构建所有包内部通过 turbo 编排见 package.json 的 build 脚本 yarn build # 仅构建指定包 yarn workspace medusajs/medusa build # 在某个包目录内进入监听模式watch mode yarn watch以 packages/modules/order/package.json 为例一个模块包的 build 脚本会先rimraf dist清空产物再通过tsc --build编译随后执行resolve:aliases用tsc-alias解析models、types等路径别名最终输出到dist目录。2.2 测试命令仓库根 package.json 提供了多层级测试入口# 运行全部单元测试turbo 编排强制无缓存执行 yarn test # 包级集成测试按 core、medusa、modules、providers 过滤 yarn test:integration:packages # HTTP 集成测试过滤 integration-tests-http yarn test:integration:http # API 集成测试 yarn test:integration:api # 模块集成测试过滤 integration-tests-modules yarn test:integration:modules集成测试还进一步划分为fast与slow两个通道test:integration:packages:fast/test:integration:packages:slowslow 通道专门覆盖 workflow-engine-redis、index、product、order、cart 这类重模块以保证 CI 资源利用更合理。2.3 数据库迁移永远不要手写迁移文件Medusa 的每个模块都有自己的 MikroORM 实体模型。每当你在packages/modules下某个模块内创建、修改或删除数据模型文件时必须在该模块包内运行迁移生成脚本禁止手写迁移文件cd packages/modules/module yarn migration:create该脚本在 packages/modules/order/package.json 中的真实定义如下migration:create: MIKRO_ORM_CLI_CONFIG./mikro-orm.config.dev.ts MIKRO_ORM_ALLOW_GLOBAL_CLItrue medusa-mikro-orm migration:create可以看到它通过MIKRO_ORM_CLI_CONFIG指定模块开发环境的 MikroORM 配置如 packages/modules/order/mikro-orm.config.dev.ts再调用medusa-mikro-orm migration:create基于当前实体定义与数据库差异自动生成迁移。同文件还提供migration:initial首次生成基线迁移、migration:up执行迁移等配套命令。迁移文件的维护原则生成后绝不手工编辑如果模型后续有变更直接重新运行yarn migration:create让脚本重新生成迁移文件需要与模型变更一并提交保证其他开发者与 CI 环境可以重放。2.4 国际化i18nSchema 生成管理后台的翻译文件位于 packages/admin/dashboard/src/i18n/translations以en.json为基准当前约 3800 行其他语言文件ar.json、de.json、fr.json、ja.json等均以英文为源进行翻译。当你在en.json中新增或删除翻译键时必须重新生成用于校验所有翻译文件的 JSON Schemacd packages/admin/dashboard yarn i18n:schema该命令对应 packages/admin/dashboard/package.json 中的i18n:schema: node ./scripts/i18n/generate-schema.js。阅读 scripts/i18n/generate-schema.js 可以看到其工作原理读取en.json并递归遍历对象树对每个 key 生成对应的 JSON Schemaproperties与required条目并设置additionalProperties: false特殊处理复数形式识别以zero/one/two/few/many/other结尾的键将其归一化为同一组复数键后一次性全部写入 schemaALL_PLURAL_FORMS数组通过 Prettier 格式化后写入translations/$schema.json。跳过该步骤的后果$schema.json是从en.json生成的会在properties和required中列出每一个键。若不同步重新生成en.json上会出现Property key is not allowed的校验警告。因此重新生成的$schema.json必须与翻译变更一起提交。三、测试约定Medusa 仓库针对不同技术栈使用两套测试框架领域框架说明后端 / 核心包Jest 29.7.0覆盖 modules、core-flows、medusa 等管理后台 / 前端Vitest 3.0.5覆盖 dashboard、design-system 等各包以自身 vitest.config.ts 实际声明为准测试文件的位置约定单元测试与源码并排的__tests__/目录包级集成测试packages/*/integration-tests/__tests__/HTTP 集成测试integration-tests/http/tests按功能分子目录如auth/、order/、product/、rbac/等模块集成测试integration-tests/modules/tests。文件命名与结构约定测试文件后缀统一使用.spec.ts或.test.ts单元测试使用describe/it块组织集成测试使用自定义测试运行器如 packages/medusa-test-utils/src/medusa-test-runner.ts自动完成数据库初始化等环境准备。四、代码风格约定4.1 格式化Prettier仓库级格式化规则非常明确提交代码前应确保符合不使用分号No semicolons字符串使用双引号缩进为 2 个空格ES5 尾随逗号箭头函数参数始终使用括号4.2 TypeScript 编译目标以 packages/modules/order/tsconfig.json 及其继承的 _tsconfig.base.json 为参考Target: ES2021Module: Node16开启严格空值检查strict null checks开启装饰器支持experimental供InjectManager等装饰器使用4.3 命名约定对象命名风格示例文件kebab-casedefine-config.ts类型 / 接口 / 类PascalCaseOrderModuleService函数 / 变量camelCasedeleteOrders常量SCREAMING_SNAKE_CASENOT_FOUND数据库字段snake_caseshipping_address_id4.4 分支命名分支名必须以类型前缀开头因为前缀会自动驱动 PR 上的 labelfeat/readable-name新功能fix/readable-nameBug 修复chore/readable-name重构、清理等docs/readable-name仅文档的 PRreadable-name必须用 kebab-case 描述该 PR 的实际变更不要只写 ticket 编号。例如应该用fix/loyalty-admin-auth-type而不是dx-2801。4.5 导出与通用约定采用 barrel 导出模式export * from特定项使用具名再导出named re-exports代码中严禁使用 emoji。五、核心架构模式Medusa 的架构可以归结为四个相互配合的模式模块服务Module Service、API 路由API Route、工作流Workflow与统一错误处理。5.1 Module 模式基于装饰器的服务每个商业模块的核心服务继承MedusaServiceT配合 TypeScript 泛型模型定义与构造函数依赖注入横切关注点通过装饰器声明。核心装饰器及其职责InjectManager()—— 注入实体管理器Entity Manager用于公开方法InjectTransactionManager()—— 注入事务管理器用于受保护方法MedusaContext()—— 将共享上下文Context作为参数注入EmitEvents()—— 操作完成后发射领域事件。以 packages/modules/order/src/services/order-module-service.ts 中真实的deleteOrders实现为例InjectTransactionManager() EmitEvents() async deleteOrders( orderIds: string | string[], MedusaContext() sharedContext: Context {} ): Promisevoid { const ids Array.isArray(orderIds) ? orderIds : [orderIds] // 查询订单关联的地址、变更记录、条目等随后级联软删除 }从这段源码可以观察到两个细节文档示例使用InjectManager()演示而当前 order 模块的实际实现采用了InjectTransactionManager()说明删除订单这类多实体级联操作被设计为在事务内执行两个装饰器在使用场景上互为补充MedusaContext() sharedContext: Context {}作为最后一个参数出现是贯穿所有模块服务方法的统一上下文传递约定调用方可以传入transactionManager实现事务透传。同样的模式也体现在 packages/modules/api-key/src/services/api-key-module-service.ts 中export class ApiKeyModuleService extends MedusaService{ ApiKey: { dto: ApiKeyTypes.ApiKeyDTO } }({ ApiKey }) implements IApiKeyModuleService { protected baseRepository_: DAL.RepositoryService // 通过构造函数注入内部服务使用 scrypt 派生密钥等 }MedusaService工厂函数基于传入的实体模型自动生成 CRUD 方法list、retrieve、create、update、softDelete、restore 等开发者只需针对业务特化逻辑覆写或扩展。该基类由medusajs/framework/utils导出。5.2 API 路由模式Medusa 的 API 路由采用文件系统路由约定在 packages/medusa/src/api 下目录层级对应 URL 路径文件名route.ts对应路径端点其中[id]形式的目录表示动态参数。路由结构要点使用 HTTP 方法的具名导出GET、POST、PUT、DELETE、PATCH请求类型使用AuthenticatedMedusaRequestT需认证或MedusaRequestT响应类型使用MedusaResponseT从req.scope解析依赖业务逻辑优先调用medusajs/core-flows中的预定义工作流而不是直接操作服务层。以订单删除路由 packages/medusa/src/api/admin/orders/route.ts 为对照列表查询路由展示了三个高频模式export const GET async ( req: AuthenticatedMedusaRequestHttpTypes.AdminOrderFilters, res: MedusaResponseHttpTypes.AdminOrderListResponse ) { const variables { filters: { ...req.filterableFields, // 过滤器 is_draft_order: false, }, ...req.queryConfig.pagination, // 分页 } const workflow getOrdersListWorkflow(req.scope) const { result } await workflow.run({ input: { fields: req.queryConfig.fields, // 字段选择 variables, }, }) // ... }而 packages/medusa/src/api/admin/payment-collections/[id]/route.ts 则展示了删除类路由的标准写法从req.params取id调用deleteOrderPaymentCollections(req.scope).run(...)最后返回统一结构的响应export const DELETE async ( req: AuthenticatedMedusaRequest, res: MedusaResponseHttpTypes.AdminDeletePaymentCollectionResponse ) { const { id } req.params await deleteOrderPaymentCollections(req.scope).run({ input: { id }, }) res.status(200).json({ id, object: payment-collection, deleted: true, }) }常见模式速查过滤器req.filterableFields分页req.queryConfig.pagination字段req.queryConfig.fields解析服务req.scope.resolve(ContainerRegistrationKeys.QUERY)5.3 Workflow 模式可补偿的业务编排Workflow工作流是 Medusa 处理复杂业务的核心抽象由medusajs/framework/workflows-sdk提供核心 API 为createStep与createWorkflow。Step 定义使用createStep(id, mainAction, compensationAction?)创建原子步骤主操作返回StepResponse(result, compensationData)补偿函数第三个参数负责失败回滚。以促销删除步骤 packages/core/core-flows/src/promotion/steps/delete-promotions.ts 为例export const deletePromotionsStep createStep( delete-promotions, async (ids: string[], { container }) { const promotionModule container.resolveIPromotionModuleService( Modules.PROMOTION ) await promotionModule.softDeletePromotions(ids) // 软删除 return new StepResponse(void 0, ids) // 第二参数作为补偿数据 }, async (idsToRestore, { container }) { // 补偿动作如果后续步骤失败恢复已删除的促销 if (!idsToRestore?.length) return const promotionModule container.resolveIPromotionModuleService( Modules.PROMOTION ) await promotionModule.restorePromotions(idsToRestore) } )注意这里的对称设计主操作调用softDeletePromotions补偿操作调用restorePromotions并通过StepResponse(void 0, ids)将ids透传给补偿函数保证回滚所需的全部数据在手。Workflow 组合使用createWorkflow(id, function)创建工作流输入用WorkflowDataT声明输出用WorkflowResponseT声明。步骤之间可以链式调用并支持transform()数据变换、when()条件分支、parallelize()并行执行、useQueryGraphStep()跨模块查询与createHook()发射事件钩子。促销删除工作流 packages/core/core-flows/src/promotion/workflows/delete-promotions.ts 展示了 hooks 的用法export const deletePromotionsWorkflow createWorkflow( delete-promotions, (input: WorkflowData{ ids: string[] }) { const deletedPromotions deletePromotionsStep(input.ids) const promotionsDeleted createHook(promotionsDeleted, { ids: input.ids, }) return new WorkflowResponse(deletedPromotions, { hooks: [promotionsDeleted], }) } )真实组合示例订单更新工作流 packages/core/core-flows/src/order/workflows/update-order.ts 是transform/when/useQueryGraphStep协同的典型实现export const updateOrderWorkflow createWorkflow( update-order-workflow, function (input: WorkflowDataOrderWorkflow.UpdateOrderWorkflowInput) { // 1. 跨模块查询订单数据 const orderQuery useQueryGraphStep({ entity: order, fields: [id, status, email, locale, shipping_address.*, ...], filters: { id: input.id }, options: { throwIfKeyNotFound: true }, }).config({ name: order-query }) const order transform({ orderQuery }, ({ orderQuery }) orderQuery.data[0]) // 2. 条件分支订单原本没有 email 且输入提供了 email 时查找/创建客户 const customerData when({ input, order }, ({ input, order }) { return !order.email !!input.email }).then(() { return findOrCreateCustomerStep({ email: input.email }) }) // 3. transform 合并地址保留原地址字段、剔除 id 以创建新地址 const updateInput transform({ input, order, customerData }, ({ input, order, customerData }) { const update: UpdateOrderDTO {} if (input.shipping_address) { const address { ...order.shipping_address, ...input.shipping_address } delete address.id update.shipping_address address } // ... return { ...input, ...update } }) const updatedOrders updateOrdersStep({ selector: { id: input.id }, update: updateInput, }) // 后续再 registerOrderChangesStep 记录变更、previewOrderChangeStep 生成预览等 } )此外该工作流中还使用了parallelize()when(locale-changed, ...).then(() parallelize(...))见 update-order.ts 第 287-290 行将相互独立的翻译同步步骤并行执行进一步提升编排效率。参考文件packages/core/core-flows/src/promotion/steps/delete-promotions.tspackages/core/core-flows/src/promotion/workflows/delete-promotions.tspackages/core/core-flows/src/order/workflows/update-order.ts5.4 错误处理MedusaError所有错误抛出一律使用new MedusaError(type, message)要求提供上下文相关、对用户友好的错误信息并在服务与工作流步骤中尽早校验输入。常用错误类型类型含义使用场景MedusaError.Types.NOT_FOUND资源不存在按 id 查询实体未命中MedusaError.Types.INVALID_DATA输入或状态无效参数格式错误、country_code 不允许变更MedusaError.Types.NOT_ALLOWED操作不被允许更新已取消的订单典型用法服务层与工作流层import { MedusaError, validateEmail } from medusajs/framework/utils // 服务层实体不存在 if (!entity) { throw new MedusaError( MedusaError.Types.NOT_FOUND, Order with id: ${id} was not found ) } // 工作流步骤校验输入 if (input.email) { validateEmail(input.email) } // 状态校验 if (order.status cancelled) { throw new MedusaError( MedusaError.Types.NOT_ALLOWED, Cannot update a cancelled order ) }MedusaError不仅在业务代码中使用基础服务层同样遵循该约定。例如 packages/core/utils/src/modules-sdk/medusa-internal-service.ts 中的retrieve实现在 id 缺失或不符合主键要求时同样抛出MedusaError.Types.NOT_FOUND保证了整个数据访问层错误语义的统一。在 packages/core/core-flows/src/order/workflows/update-order.ts 的updateOrderValidationStep中可以直观看到这三类校验的完整组合先throwIfOrderIsCancelled拒绝已取消订单再校验地址country_code不可变更抛INVALID_DATA最后对input.email执行validateEmail。5.5 常见导入模式路径别名在各模块 tsconfig.json 的paths中配置别名指向用途models./src/models实体模型types./src/typesDTO 与类型定义services./src/services服务依赖repositories./src/repositories数据访问层utils./src/utils工具函数框架层导入统一从medusajs/framework/*引入// 工具与装饰器 import { InjectManager, InjectTransactionManager, MedusaContext, MedusaError, MedusaService, EmitEvents, Modules, } from medusajs/framework/utils // 类型 import type { Context, DAL, IOrderModuleService, } from medusajs/framework/types // 工作流 SDK import { WorkflowData, WorkflowResponse, createStep, createWorkflow, transform, } from medusajs/framework/workflows-sdk // 预定义工作流 import { deleteOrderWorkflow } from medusajs/core-flows // HTTP 层 import { AuthenticatedMedusaRequest, MedusaResponse, } from medusajs/framework/http六、总结一份为 Agent 与开发者准备的仓库协作规范Medusa Core 的 CLAUDE.md 本质上是一份面向 AI 编程助手与开发者的仓库协作指南它把 monorepo 中最容易踩坑的规则显式化结构层面packages/medusaAPI 路由、packages/core/*框架与工作流、packages/modules/*业务模块职责边界清晰流程层面迁移必须由脚本生成、i18n schema 必须随翻译变更同步更新、分支前缀驱动 PR label代码层面统一的装饰器注入、MedusaService基类、createStep/createWorkflow编排、MedusaError错误语义使得任意开发者或 Agent都能在阅读少量文件后推断出整个系统的扩展方式。对于想要为 Medusa 贡献代码或基于它进行二次开发的团队建议按以下路径上手先通过yarn install yarn build跑通本地构建再用yarn workspace medusajs/order migration:create体验一次迁移生成流程最后对照 order-module-service.ts、update-order.ts 与 route.ts 三个文件即可完整串联起模块服务 → 工作流 → API 路由的 Medusa 核心调用链。【免费下载链接】medusaThe worlds most flexible commerce platform for agents and developers项目地址: https://gitcode.com/GitHub_Trending/me/medusa创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考