使用六边形架构和领域驱动设计构建可维护的Python应用程序

在当今快速发展的软件开发环境中,构建易于维护、适应和扩展的应用程序至关重要。六边形架构(也称为端口与适配器)和领域驱动设计(DDD)是应对这些挑战的有效组合。六边形架构促进了关注点的清晰分离,使得在不干扰核心逻辑的情况下,更容易替换、测试或增强系统的某些部分。同时,DDD专注于将代码与现实世界的商业概念对齐,确保系统既直观又具有弹性。这两种方法结合在一起,使开发人员能够构建出强大、灵活且设计上能够无缝适应变化需求和未来增长的系统。

1. 六边形架构简介

六边形架构,也称为端口与适配器模式,由阿利斯泰尔·科克本提出,以解决传统分层架构的僵化和复杂性。其主要目标是使应用程序的核心逻辑(领域)独立于外部系统,从而实现更容易的测试、维护和适应性。

六边形架构的核心是将应用程序分为三个主要层次:

  • 核心(业务逻辑/领域): 系统的核心,包含业务规则和领域逻辑。该层是独立的,不依赖于外部库或框架。
    示例: 计算贷款利息或根据业务规则验证用户的操作。
  • 端口(接口): 核心与外部世界交互的抽象定义(例如,接口或协议)。端口代表用例或特定应用程序的API。它们定义需要做什么,而不指定如何做
    示例: 仓库端口定义与数据源交互的方法,如:

    • get(id: ID): Entity: 通过唯一标识符检索实体。
    • insert(entity: Entity): void: 添加一个新实体。
    • update(entity: Entity): void: 更新一个现有实体。
src/ports/repository.py
from abc import ABC, abstractmethod
from typing import List
from src.entities import Entity

class Repository(ABC):
    @abstractmethod
    def get(self, id: str) -> Entity:
        pass

    @abstractmethod
    def insert(self, entity: Entity) -> None:
        pass

    @abstractmethod
    def update(self, entity: Entity) -> None:
        pass
  • 适配器(实现): 端口的具体实现。它们处理与外部系统(如数据库、API或用户界面)的实际交互。
    示例: PostgresRepository适配器使用SQLAlchemy实现了PostgreSQL的Repository端口。
# src/adapters/postgres_repository.py
from sqlalchemy import create_engine, Column, String
from sqlalchemy.orm import declarative_base, sessionmaker
from src.entities import Entity
from src.ports.repository import Repository

Base = declarative_base()

# 定义Entity的数据库表
class EntityModel(Base):
    __tablename__ = "entities"
    id = Column(String, primary_key=True)
    name = Column(String, nullable=False)

description = Column(String)

class PostgresRepository(Repository):
    def __init__(self, db_url: str):
        """
        使用 PostgreSQL 连接 URL 初始化仓库。
        示例 db_url: "postgresql+psycopg2://username:password@host:port/dbname"
        """
        self.engine = create_engine(db_url)
        Base.metadata.create_all(self.engine)
        self.Session = sessionmaker(bind=self.engine)

    def get(self, id: str) -> Entity:
        session = self.Session()
        try:
            entity_model = session.query(EntityModel).filter_by(id=id).first()
            if not entity_model:
                raise ValueError(f"未找到 ID 为 {id} 的实体")
            return Entity(id=entity_model.id, name=entity_model.name, description=entity_model.description)
        finally:
            session.close()

    def insert(self, entity: Entity) -> None:
        session = self.Session()
        try:
            entity_model = EntityModel(id=entity.id, name=entity.name, description=entity.description)
            session.add(entity_model)
            session.commit()
        finally:
            session.close()

    def update(self, entity: Entity) -> None:
        session = self.Session()
        try:
            entity_model = session.query(EntityModel).filter_by(id=entity.id).first()
            if not entity_model:
                raise ValueError(f"未找到 ID 为 {entity.id} 的实体")
            entity_model.name = entity.name
            entity_model.description = entity.description
            session.commit()
        finally:
            session.close()

架构通常被形象化为一个六边形,象征着与核心交互的多种方式,每一侧代表一个不同的适配器或端口。

图像描述

2. 领域驱动设计(DDD)简介

领域驱动设计(DDD)是一种软件设计方法,强调业务目标与为实现这些目标而构建的软件之间的紧密对齐。这种方法论由埃里克·埃文斯在他的著作《领域驱动设计:应对软件核心复杂性》中提出。

DDD的核心在于与领域专家合作,理解和建模领域(即业务问题空间),并将这种理解转化为软件系统。DDD提倡领域的解耦,确保系统的不同部分保持独立、清晰且易于管理。
领域驱动设计的关键概念:

  • 领域: 软件所涉及的特定知识或活动领域。例如,在银行应用程序中,领域包括账户、交易和客户等概念。
  • 普遍语言: 开发人员和领域专家共同开发的通用语言。这种共享词汇确保所有利益相关者之间的清晰沟通和一致理解。
  • 实体和值对象:
    • 实体: 具有独特身份和生命周期的对象,例如客户或订单。
    • 值对象: 由其属性而非独特身份定义的不可变对象,如日期或货币金额。
  • 聚合: 相关实体和值对象的集群,作为数据变更的单一单位处理。每个聚合都有一个根实体,以确保整个集群的完整性。
  • 仓库: 用于检索和存储聚合的机制,为数据访问提供了一层抽象。
  • 服务: 不自然适合于实体或值对象的操作或过程,但对领域至关重要,例如处理支付。
domain/
│
├── user/
│   ├── entities/
│   │   └── user.py
│   │
│   ├── value_objects/
│   │   └── email.py
│   │
│   ├── events/
│   │   └── user_created_event.py
│   │
│   ├── services/
│   │   └── user_service.py
│   │
│   └── repositories/
│       └── user_repository_interface.py
│
├── product/
│   ├── entities/
│   │   └── product.py
│   │
│   ├── value_objects/
│   │   └── price.py
│   │
│   ├── events/
│   │   └── product_created_event.py
│   │
│   ├── services/
│   │   └── product_service.py
│   │
│   └── repositories/
│       └── product_repository_interface.py

在本节中,我不会提供实施领域驱动设计(DDD)的详细示例,因为它是一种全面的方法论,主要集中于解决复杂的业务逻辑挑战。DDD擅长于结构化和管理复杂的业务规则,但要充分发挥其潜力并解决其他编码问题,最好在一个互补的架构框架内使用。因此,在接下来的部分中,领域驱动设计将与六边形架构相结合,以突出其优势,并为解决超越业务逻辑的其他编码问题提供坚实的基础,并附带详细示例。

3. 六边形架构与领域驱动设计如何相互补充

为什么选择六边形架构和领域驱动设计?

领域驱动设计(DDD)和六边形架构相辅相成,强调清晰的边界并使软件与业务需求保持一致。DDD专注于建模核心领域并隔离业务逻辑,而六边形架构则通过端口和适配器确保这些逻辑独立于外部系统。它们解决了不同但互补的关注点:

  • 六边形架构作为框架:
    • 六边形架构定义了整体系统的组织方式以及不同部分(例如领域、基础设施、用户界面)之间的交互。
    • 它提供了一个环境,使领域逻辑能够独立于外部关注点运行,从而摆脱基础设施细节的束缚。
  • 领域驱动设计作为核心逻辑:
    • DDD通过确保业务逻辑不仅被封装,而且反映真实的业务需求,丰富了六边形架构所定义的核心领域。
    • 它专注于如何有效地设计和实现领域层,确保其保持意义和适应性。

两者结合,使得系统可扩展、可测试且灵活,领域始终是中心焦点,免受基础设施或技术变化的影响。这种协同作用确保了一个强健的设计,能够轻松适应不断变化的业务需求。

以下部分提供了一个实际示例,展示了领域驱动设计(DDD)和六边形架构如何协同工作,以创建稳健、可维护和适应性强的软件系统。

### 实际示例
该项目应用六边形架构和领域驱动设计(DDD),旨在创建可扩展和可维护的系统,为应用程序开发提供现代且稳健的基础。该项目使用Python构建,采用FastAPI作为Web框架,DynamoDB作为数据库。

项目结构如下:


python-hexagonal-ddd/
├── src/                          # 应用程序的源代码
│   ├── __init__.py               # 包标记
│   ├── main.py                   # 应用程序的入口点
│   ├── application/              # 应用服务层
│   │   ├── __init__.py
│   │   ├── product_service.py    # 产品相关操作的服务
│   │   └── user_service.py       # 用户相关操作的服务
│   ├── domains/                  # 领域层
│   │   ├── product/              # 产品领域
│   │   │   ├── __init__.py
│   │   │   ├── aggregates.py     # 产品领域的聚合根
│   │   │   ├── entities.py       # 产品领域的实体
│   │   │   ├── exceptions.py     # 产品领域的异常
│   │   │   └── value_objects.py  # 产品领域的值对象
│   │   ├── user/                 # 用户领域
│   │   │   ├── __init__.py
│   │   │   ├── aggregates.py     # 用户领域的聚合根
│   │   │   ├── entities.py       # 用户领域的实体
│   │   │   ├── exceptions.py     # 用户领域的异常
│   │   │   └── value_objects.py  # 用户领域的值对象
│   ├── infrastructure/           # 基础设施层
│   │   ├── logging/              # 日志基础设施
│   │   │   └── logging_adapter.py # 日志适配器
│   │   ├── middleware/           # 中间件组件
│   │   │   └── trace_id.py       # 跟踪ID的中间件
│   │   ├── utils/                # 工具函数
│   │   │   └── context.py        # 上下文工具
│   │   ├── __init__.py
│   │   ├── dynamodb_product_repository.py # 产品的DynamoDB仓库
│   │   └── dynamodb_user_repository.py    # 用户的DynamoDB仓库
│   ├── ports/                    # 端口层
│   │   ├── logger/               # 日志端口
│   │   │   └── logger_port.py    # 日志端口接口
│   │   ├── __init__.py
│   │   ├── product_repository.py  # 产品仓库接口
│   │   └── user_repository.py     # 用户仓库接口
│   ├── routers/                  # API路由器
│   │   ├── product.py            # 产品端点的路由器
│   │   └── user.py               # 用户端点的路由器
├── tests/                        # 测试套件
│   ├── test_product_service.py   # 产品服务的测试
│   └── test_user_service.py      # 用户服务的测试
├── .env.demo                     # 示例环境变量文件
├── requirements.txt              # 项目依赖
└── README.md                     # 项目文档

您可以在我的 GitHub 仓库中找到 [源代码](https://github.com/HieuTranV/python-hexagonal-ddd)。

4. 结论

将六边形架构和领域驱动设计(DDD)融入Python应用程序,有助于开发出可维护、可适应并与业务目标紧密对齐的系统。六边形架构确保核心业务逻辑与外部系统之间的明确分离,促进了灵活性和测试的便利性。DDD强调准确建模领域,从而使软件真正反映业务流程和规则。通过整合这些方法论,开发人员可以创建出不仅满足当前需求,而且能够很好地适应未来业务需求的强大应用程序。

更多