基本认知

  • 没有银弹,没有最好的架构,只有最适合的架构。脱离具体业务场景空谈某一个架构好坏,没有任何意义。
  • 架构设计需要遵循三个主要原则:合适原则、简单原则、演化原则。
    • 合适原则 : 合适优于业界领先,不追求高大上方案,只追求最合适的。
    • 简单原则: KISS原则(Keep It Simple, Stupid!),简单优于复杂。
    • 演化原则:演化优于一步到位,不要过度设计,不要提前优化,不要为了优化而优化。而对于软件来说,变化才是主题。软件架构需要根据业务的发展而不断变化。
  • 关于分层,无论是 DDD 战术设计中提出的四层架构,还是简单的三层架构,本质都是讲如何去做结构划分。分层能起到代码复用、隔离变化、隔离关注点的作用。还可以提高代码的可测试性和应对系统的复杂性。

架构总览

工程架构:

image.png

项目分层

Interfaces(用户接口层)

  • 接收和响应用户请求 :它处理来自用户的HTTP请求、RPC调用或其他形式的输入。
  • 数据转换 :将外部传入的数据(如JSON)转换为内部应用层或领域层可以理解的数据传输对象(DTOs)或值对象(Value Objects)。同样,它也将内部模型的输出转换为外部世界(如用户浏览器)期望的格式。
  • 错误处理和展示 :捕获应用层或领域层抛出的异常,并将它们转换为对用户友好的错误信息和适当的 HTTP 状态码。
  • 协议与技术解耦:对 Application 层和 Domain 层屏蔽服务框架(Hertz、KiteX)细节,保证领域纯净性。
  • 跨切面关注点(AOP/Middleware):
    • 可以在此层统一日志记录、性能监控、限流降级、分布式追踪等基础设施功能。

Application(应用层)

  • 参数校验,权限校验:
    • 细粒度的权限控制(例如,检查当前用户是否有权限发布这个特定的Agent)通常在应用层中处理。
  • 业务流程编排:
    • 协调多个领域对象(实体、聚合、领域服务)完成业务逻辑,完成业务用例(Use Case)或用户故事(User Story)。
    • 举例:发布 Agent 的时候,需要同时发布 Agent 的 Variable 、Plugin、DataBase、Shortcut。
    • 关键点:仅定义业务流程,不实现具体业务逻辑(如具体实体的发布逻辑属于领域层)。
  • 发布与订阅领域事件:
    • 通过消息总线或事件调度器,发布在领域模型中产生的领域事件;
    • 在本层或更上层负责订阅这些事件,触发后续处理(异步通知、补偿流程、日志记录等)。
  • 事务处理:
    • 在用例边界处开启、提交或回滚数据库事务,保证一组操作要么全部成功,要么全部失败;
  • DTO 与数据映射:
    • 将接口层传入的 DTO/VO 转换为领域层可识别的参数;
    • 将领域层返回的领域对象或值对象转换为接口层需要的 DTO,便于序列化输出。

Domain(领域层)

  • 领域模型(Entities )
    • 实体(Entity):充血模型 ,具有唯一标识(ID)的业务对象,封装状态与行为,例如 Agent、Plugin、Variable 等;
  • 领域服务(Domain Service)
    • 实现跨实体或跨聚合的业务规则。
    • Domain Service 类负责与 Repository 交流。之所以让 Domain Service 与 Repository 打交道,而不是让领域模型与 Repository 打交道,那是因为我们想保持领域模型的独立性,不与任何其他层的代码(Repository 层的代码)耦合在一起,将流程性的代码逻辑(比如从 DB 中取数据、映射数据)与领域模型的业务逻辑解耦,让领域模型更加可复用。
    • Domain Service 应该相互独立,Domain Service里面不能直接依赖其他 Domain Service 的服务。如果需要在一个 Domain Service 中访问另外一个 Domain Service,需要用 CrossDomain 方式调用。
  • 仓储接口(Repository Interface)
    • 提供一个数据存储接口集合,用于访问和持久化聚合根。它将领域层与底层的数据存储技术(如数据库)解耦。
    • 默认的 Repository Interface 的实现也是写在 Domain 层,让 Domain 逻辑更内聚。
    • 在程序启动的时候通过 DI 方式注入。
  • 领域接口( Domain Interface)
    • 提供了当前领域所有能力的接口集合,主要对外给 Application 层和 CrossDomain 层接口使用。
    • 在程序启动的时候通过 DI 方式注入。
  • Infrastructure-Contract(Infra 组件抽象)
    • 由于需要多云适配,所以对依赖的基础组件做了一层抽象,比如 ElasticSearch、MQ、Cache、文件存储等。Domain 只依赖 Infra 的抽象,不依赖具体实现。

CrossDomain(跨域访问)

  • CrossDomain 作为一个防腐层,避免 Domain 之间直接互相依赖,而是通过一层抽象,让 Domain 之间只依赖接口协议,而不依赖具体的实现。
  • CrossDomain 的 Interface 是 Domain Interface 的子集,但是通过适配器模式来屏蔽了对 Domain 核心 Entity 的依赖。
  • 在程序启动的时候通过 DI 方式注入。

Infrastructure(基础层)

  • 具体基础的实现比如(Redis、ES7、ES8、RMQ、Kafka、文件存储、具体 LLM model),在不同的云服务中可以提供不同的基础组件实现,以提供更好的多云兼容性。