在当今快速发展的软件开发环境中,构建易于维护、适应和扩展的应用程序至关重要。六边形架构(也称为端口与适配器)和领域驱动设计(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强调准确建模领域,从而使软件真正反映业务流程和规则。通过整合这些方法论,开发人员可以创建出不仅满足当前需求,而且能够很好地适应未来业务需求的强大应用程序。