欢迎光临
我们一直在努力

DDD-021:Spring Boot + DDD 项目结构

DDD-021:Spring Boot + DDD 项目结构

21.1 项目结构概述

21.1.1 为什么项目结构如此重要?

【原理】

项目结构是软件架构的外在表现,它直接影响:

  • 代码可读性:清晰的结构让人快速定位代码
  • 团队协作:明确的边界减少冲突
  • 架构演进:良好的结构支持渐进式重构
  • 知识传递:结构本身就是文档
  • DDD 强调项目结构应该反映领域模型,而不是技术实现。一个好的 DDD 项目结构应该让新人一眼就能看出系统的业务领域。

    【历史架构问题】

    传统 Spring Boot 项目的结构问题:

    ❌ 传统技术分层结构

    src/main/java/com/example/
    ├── controller/ # 所有 Controller 混在一起
    │ ├── OrderController.java
    │ ├── UserController.java
    │ ├── ProductController.java
    │ └── PaymentController.java
    ├── service/ # 所有 Service 混在一起
    │ ├── OrderService.java
    │ ├── UserService.java
    │ └── ProductService.java
    ├── dao/ # 所有 DAO 混在一起
    │ ├── OrderDao.java
    │ ├── UserDao.java
    │ └── ProductDao.java
    ├── entity/ # 贫血实体
    │ ├── Order.java
    │ ├── User.java
    │ └── Product.java
    ├── dto/ # 所有 DTO 混在一起
    │ ├── OrderDTO.java
    │ └── UserDTO.java
    └── util/ # 工具类
    └── DateUtil.java

    存在问题:

    问题
    表现
    影响
    业务边界模糊 订单、用户、商品代码混杂 难以理解业务
    修改范围大 改一个功能涉及多个目录 重构成本高
    团队冲突 多人修改同一目录 合并频繁冲突
    领域模型弱化 entity 包只有贫血对象 业务逻辑散落

    真实案例:某电商平台

    初期团队5人,按技术分层开发
    ├── 代码量:10万行
    ├── 问题:业务逻辑分散在 Service 层
    ├── 6个月后:新功能开发变慢,Bug 增加
    └── 1年后:不敢改动代码,怕引入 Bug

    根本原因:项目结构没有体现业务边界

    【DDD 如何解决】

    DDD 推荐的项目结构按业务领域组织,而非技术分层:

    ✅ DDD 领域驱动结构

    src/main/java/com/example/
    ├── order/ # 订单上下文(一个完整的业务边界)
    │ ├── api/ # 接口层
    │ │ ├── controller/
    │ │ ├── dto/
    │ │ └── assembler/
    │ ├── application/ # 应用层
    │ │ ├── service/
    │ │ └── command/
    │ ├── domain/ # 领域层(核心)
    │ │ ├── model/
    │ │ ├── repository/
    │ │ ├── service/
    │ │ └── event/
    │ └── infrastructure/ # 基础设施层
    │ ├── repository/
    │ ├── gateway/
    │ └── config/
    ├── product/ # 商品上下文
    │ ├── api/
    │ ├── application/
    │ ├── domain/
    │ └── infrastructure/
    ├── user/ # 用户上下文
    └── payment/ # 支付上下文

    【设计优势】

    维度
    技术分层
    领域驱动结构
    业务边界 模糊 清晰
    代码定位 需要记忆目录 按业务直觉定位
    团队协作 冲突频繁 按上下文分配
    重构范围 牵一发动全身 影响单个上下文
    新人理解 需要阅读代码 看目录结构即懂

    21.2 项目模块划分策略

    21.2.1 单模块 vs 多模块

    【原理】

    Spring Boot 项目有两种模块划分策略:

  • 单模块结构:所有代码在一个 Maven 模块中
  • 多模块结构:按上下文拆分为多个 Maven 模块
  • 选择依据:

    团队规模
    项目阶段
    推荐结构
    5人以下 初期 单模块
    5-10人 发展中 单模块 + 包隔离
    10人以上 成熟 多模块
    微服务 任意 每个服务独立模块

    【历史架构问题】

    两种极端:

    ❌ 极端1:过早多模块(团队3人)

    ecommerce/
    ├── order-module/
    ├── product-module/
    ├── user-module/
    ├── payment-module/
    └── common-module/

    问题:
    1. 构建时间长(每个模块独立编译)
    2. 模块间依赖复杂
    3. 调试困难(跨模块断点)
    4. 团队规模小,维护成本高

    ❌ 极端2:始终单模块(团队30人)

    ecommerce/
    └── src/main/java/com/example/
    └── … 100万行代码

    问题:
    1. 编译时间超长(10分钟+)
    2. 部署风险大(改一行重新部署)
    3. 团队协作冲突频繁
    4. 无法独立扩展

    【DDD 如何解决】

    渐进式模块化策略:

    阶段1:单模块 + 包隔离(0-6个月,团队5-10人)

    ecommerce-app/
    ├── src/main/java/com/example/
    │ ├── order/ # 订单上下文(包)
    │ ├── product/ # 商品上下文(包)
    │ └── user/ # 用户上下文(包)
    └── pom.xml

    特点:
    – 快速开发
    – 清晰的业务边界
    – 低维护成本

    阶段2:模块化单体(6-12个月,团队10-20人)

    ecommerce-app/
    ├── order-module/ # 订单模块
    │ ├── src/main/java/
    │ └── pom.xml
    ├── product-module/ # 商品模块
    ├── user-module/ # 用户模块
    ├── common-module/ # 公共模块
    └── pom.xml (父POM)

    特点:
    – 模块间依赖明确
    – 可以独立测试
    – 为微服务做准备

    阶段3:微服务(12个月+,团队20+人)

    ecommerce-system/
    ├── order-service/ # 订单微服务
    │ ├── src/
    │ └── pom.xml
    ├── product-service/ # 商品微服务
    ├── user-service/ # 用户微服务
    └── pom.xml (父POM)

    特点:
    – 独立部署
    – 独立扩展
    – 团队独立自治

    【设计优势】

    维度
    过早多模块
    渐进式模块化
    初期开发效率
    模块边界清晰度 低(需要提前设计) 高(实践中验证)
    团队适应度 需要学习 渐进学习
    技术债务 高(设计过度) 低(按需演进)

    21.2.2 包结构设计原则

    【原理】

    DDD 项目包结构的核心原则:

  • 按领域分包:优先按业务领域组织,其次按技术分层
  • 高内聚低耦合:同一上下文的代码放在一起
  • 依赖倒置:领域层不依赖基础设施层
  • 两种常见的包结构:

    方式1:按技术分层分包(在领域包内部)

    com.example.order/ # 订单上下文
    ├── api/ # 接口层
    │ ├── controller/
    │ │ └── OrderController.java
    │ ├── dto/
    │ │ ├── CreateOrderRequest.java
    │ │ └── OrderResponse.java
    │ └── assembler/
    │ └── OrderAssembler.java
    ├── application/ # 应用层
    │ ├── service/
    │ │ └── OrderApplicationService.java
    │ ├── command/
    │ │ ├── CreateOrderCommand.java
    │ │ └── PayOrderCommand.java
    │ └── query/
    │ └── OrderQueryService.java
    ├── domain/ # 领域层(核心)
    │ ├── model/
    │ │ ├── Order.java # 聚合根
    │ │ ├── OrderItem.java # 实体
    │ │ ├── OrderId.java # 值对象
    │ │ └── OrderStatus.java # 枚举
    │ ├── repository/
    │ │ └── OrderRepository.java
    │ ├── service/
    │ │ └── PricingService.java
    │ └── event/
    │ ├── OrderCreatedEvent.java
    │ └── OrderPaidEvent.java
    └── infrastructure/ # 基础设施层
    ├── repository/
    │ ├── OrderJpaRepository.java
    │ ├── JpaOrderRepository.java
    │ └── OrderPO.java
    ├── gateway/
    │ └── ProductGatewayImpl.java
    └── config/
    └── OrderConfig.java

    方式2:按领域概念分包(更推荐)

    com.example.order/ # 订单上下文
    ├── application/ # 应用层
    │ ├── OrderApplicationService.java
    │ ├── CreateOrderCommand.java
    │ └── PayOrderCommand.java
    ├── domain/ # 领域层(核心)
    │ ├── Order.java # 聚合根
    │ ├── OrderItem.java # 实体
    │ ├── OrderId.java # 值对象
    │ ├── OrderRepository.java # 仓储接口
    │ └── OrderStatus.java # 枚举
    ├── infrastructure/ # 基础设施层
    │ ├── persistence/
    │ │ ├── OrderJpaRepository.java
    │ │ ├── JpaOrderRepository.java
    │ │ └── OrderPO.java
    │ └── gateway/
    │ └── ProductGatewayImpl.java
    └── interfaces/ # 接口层
    ├── controller/
    │ └── OrderController.java
    ├── dto/
    │ ├── CreateOrderRequest.java
    │ └── OrderResponse.java
    └── assembler/
    └── OrderAssembler.java

    【历史架构问题】

    ❌ 传统技术分包(跨领域)

    com.example/
    ├── controller/
    │ ├── OrderController.java # 订单
    │ ├── UserController.java # 用户(跨领域混在一起)
    │ └── ProductController.java # 商品
    ├── service/
    │ ├── OrderService.java
    │ ├── UserService.java
    │ └── ProductService.java
    └── dao/
    ├── OrderDao.java
    └── UserDao.java

    问题:
    1. 改一个订单功能,需要跳转多个目录
    2. 删除一个功能,需要搜索整个项目
    3. 新人理解业务困难,代码分散

    【DDD 如何解决】

    ✅ DDD 领域分包(内聚)

    com.example/
    ├── order/ # 订单上下文(内聚)
    │ ├── application/
    │ │ └── OrderApplicationService.java
    │ ├── domain/
    │ │ ├── Order.java
    │ │ └── OrderRepository.java
    │ ├── infrastructure/
    │ │ └── JpaOrderRepository.java
    │ └── interfaces/
    │ ├── OrderController.java
    │ └── CreateOrderRequest.java
    ├── user/ # 用户上下文
    │ ├── application/
    │ ├── domain/
    │ ├── infrastructure/
    │ └── interfaces/
    └── product/ # 商品上下文

    优势:
    1. 改订单功能,只需要在 order 包下修改
    2. 删除订单功能,删除 order 包即可
    3. 新人看包名就知道业务

    【代码示例】

    // ========== 包结构示例:订单上下文 ==========

    // domain/model/Order.java – 聚合根
    package com.example.order.domain;

    public class Order extends AggregateRoot<OrderId> {


    private OrderId id;
    private OrderStatus status;
    private List<OrderItem> items;

    public static Order create(CustomerId customerId, List<OrderItem> items) {


    Order order = new Order();
    order.id = OrderId.generate();
    order.status = OrderStatus.CREATED;
    order.items = items;
    return order;
    }

    public void pay(PaymentId paymentId) {


    if (this.status != OrderStatus.CREATED) {


    throw new InvalidOrderStateException(\”只能支付待支付的订单\”);
    }
    this.status = OrderStatus.PAID;
    this.registerEvent(new OrderPaidEvent(this.id, paymentId));
    }
    }

    // domain/repository/OrderRepository.java – 仓储接口
    package com.example.order.domain;

    public interface OrderRepository {


    Order findById(OrderId id);
    void save(Order order);
    List<Order> findByCustomerId(CustomerId customerId);
    }

    // application/OrderApplicationService.java – 应用服务
    package com.example.order.application;

    @Service
    @Transactional
    public class OrderApplicationService {


    private final OrderRepository orderRepository;

    public OrderId createOrder(CreateOrderCommand command) {


    Order order = Order.create(
    command.getCustomerId(),
    command.getItems()
    );
    orderRepository.save(order);
    return order.getId();
    }
    }

    // infrastructure/persistence/JpaOrderRepository.java – 仓储实现
    package com.example.order.infrastructure.persistence;

    @Repository
    public class JpaOrderRepository implements OrderRepository {


    @Autowired
    private OrderJpaRepository jpaRepository;

    @Override
    public Order findById(OrderId id) {


    return jpaRepository.findById(id.getValue())
    .map(OrderMapper::toDomain)
    .orElse(null);
    }
    }

    // interfaces/controller/OrderController.java – 控制器
    package com.example.order.interfaces.controller;

    @RestController
    @RequestMapping(\”/api/orders\”)
    public class OrderController {


    private final OrderApplicationService orderService;

    @PostMapping
    public OrderId createOrder(@RequestBody CreateOrderRequest request) {


    CreateOrderCommand command = OrderAssembler.toCommand(request);
    return orderService.createOrder(command);
    }
    }


    21.3 完整项目结构示例

    21.3.1 单模块项目结构

    【代码示例】

    ecommerce-app/ # 项目根目录
    ├── pom.xml # Maven 配置
    ├── src/
    │ ├── main/
    │ │ ├── java/com/example/ecommerce/
    │ │ │ ├── EcommerceApplication.java # 启动类
    │ │ │ │
    │ │ │ ├── order/ # ========== 订单上下文 ==========
    │ │ │ │ ├── application/ # 应用层
    │ │ │ │ │ ├── OrderApplicationService.java
    │ │ │ │ │ ├── command/
    │ │ │ │ │ │ ├── CreateOrderCommand.java
    │ │ │ │ │ │ ├── PayOrderCommand.java
    │ │ │ │ │ │ └── CancelOrderCommand.java
    │ │ │ │ │ └── query/
    │ │ │ │ │ └── OrderQueryService.java
    │ │ │ │ │
    │ │ │ │ ├── domain/ # 领域层(核心)
    │ │ │ │ │ ├── model/
    │ │ │ │ │ │ ├── Order.java # 聚合根
    │ │ │ │ │ │ ├── OrderItem.java # 实体
    │ │ │ │ │ │ ├── OrderId.java # 值对象
    │ │ │ │ │ │ └── OrderStatus.java# 枚举
    │ │ │ │ │ ├── repository/
    │ │ │ │ │ │ └── OrderRepository.java
    │ │ │ │ │ ├── service/
    │ │ │ │ │ │ └── PricingService.java
    │ │ │ │ │ └── event/
    │ │ │ │ │ ├── OrderCreatedEvent.java
    │ │ │ │ │ └── OrderPaidEvent.java
    │ │ │ │ │
    │ │ │ │ ├── infrastructure/ # 基础设施层
    │ │ │ │ │ ├── persistence/
    │ │ │ │ │ │ ├── OrderJpaRepository.java
    │ │ │ │ │ │ ├── JpaOrderRepository.java
    │ │ │ │ │ │ ├── OrderPO.java
    │ │ │ │ │ │ └── OrderMapper.java
    │ │ │ │ │ ├── gateway/
    │ │ │ │ │ │ ├── ProductGatewayImpl.java
    │ │ │ │ │ │ └── PaymentGatewayImpl.java
    │ │ │ │ │ └── config/
    │ │ │ │ │ └── OrderConfig.java
    │ │ │ │ │
    │ │ │ │ └── interfaces/ # 接口层
    │ │ │ │ ├── controller/
    │ │ │ │ │ └── OrderController.java
    │ │ │ │ ├── dto/
    │ │ │ │ │ ├── request/
    │ │ │ │ │ │ ├── CreateOrderRequest.java
    │ │ │ │ │ │ └── PayOrderRequest.java
    │ │ │ │ │ └── response/
    │ │ │ │ │ ├── OrderResponse.java
    │ │ │ │ │ └── OrderListResponse.java
    │ │ │ │ └── assembler/
    │ │ │ │ └── OrderAssembler.java
    │ │ │ │
    │ │ │ ├── product/ # ========== 商品上下文 ==========
    │ │ │ │ ├── application/
    │ │ │ │ ├── domain/
    │ │ │ │ ├── infrastructure/
    │ │ │ │ └── interfaces/
    │ │ │ │
    │ │ │ ├── inventory/ # ========== 库存上下文 ==========
    │ │ │ │ ├── application/
    │ │ │ │ ├── domain/
    │ │ │ │ ├── infrastructure/
    │ │ │ │ └── interfaces/
    │ │ │ │
    │ │ │ ├── payment/ # ========== 支付上下文 ==========
    │ │ │ │ ├── application/
    │ │ │ │ ├── domain/
    │ │ │ │ ├── infrastructure/
    │ │ │ │ └── interfaces/
    │ │ │ │
    │ │ │ ├── user/ # ========== 用户上下文 ==========
    │ │ │ │ ├── application/
    │ │ │ │ ├── domain/
    │ │ │ │ ├── infrastructure/
    │ │ │ │ └── interfaces/
    │ │ │ │
    │ │ │ └── common/ # ========== 公共模块 ==========
    │ │ │ ├── domain/
    │ │ │ │ ├── AggregateRoot.java
    │ │ │ │ ├── Entity.java
    │ │ │ │ ├── ValueObject.java
    │ │ │ │ └── DomainEvent.java
    │ │ │ ├── exception/
    │ │ │ │ ├── BusinessException.java
    │ │ │ │ └── EntityNotFoundException.java
    │ │ │ └── util/
    │ │ │ └── Money.java
    │ │ │
    │ │ └── resources/
    │ │ ├── application.yml # 主配置文件
    │ │ ├── application-dev.yml # 开发环境配置
    │ │ ├── application-test.yml # 测试环境配置
    │ │ ├── application-prod.yml # 生产环境配置
    │ │ └── db/
    │ │ └── migration/ # 数据库迁移脚本
    │ │
    │ └── test/java/com/example/ecommerce/
    │ ├── order/
    │ │ ├── domain/
    │ │ │ └── OrderTest.java # 领域层单元测试
    │ │ └── application/
    │ │ └── OrderApplicationServiceTest.java
    │ └── product/

    └── docs/ # 项目文档
    ├── architecture.md # 架构说明
    ├── domain-model.md # 领域模型说明
    └── api/ # API 文档
    └── order-api.md

    21.3.2 多模块项目结构

    【代码示例】

    ecommerce-platform/ # 项目根目录
    ├── pom.xml # 父 POM

    ├── ecommerce-common/ # 公共模块
    │ ├── pom.xml
    │ └── src/main/java/com/example/common/
    │ ├── domain/
    │ │ ├── AggregateRoot.java
    │ │ ├── Entity.java
    │ │ ├── ValueObject.java
    │ │ └── DomainEvent.java
    │ ├── exception/
    │ │ ├── BusinessException.java
    │ │ └── EntityNotFoundException.java
    │ └── util/
    │ ├── Money.java
    │ └── AuditInfo.java

    ├── ecommerce-order/ # 订单模块
    │ ├── pom.xml
    │ ├── src/main/java/com/example/order/
    │ │ ├── application/
    │ │ ├── domain/
    │ │ ├── infrastructure/
    │ │ └── interfaces/
    │ └── src/test/java/com/example/order/

    ├── ecommerce-product/ # 商品模块
    │ ├── pom.xml
    │ └── src/main/java/com/example/product/

    ├── ecommerce-inventory/ # 库存模块
    │ ├── pom.xml
    │ └── src/main/java/com/example/inventory/

    ├── ecommerce-payment/ # 支付模块
    │ ├── pom.xml
    │ └── src/main/java/com/example/payment/

    ├── ecommerce-user/ # 用户模块
    │ ├── pom.xml
    │ └── src/main/java/com/example/user/

    └── ecommerce-web/ # Web 应用(聚合所有模块)
    ├── pom.xml
    └── src/main/java/com/example/
    ├── EcommerceApplication.java # 启动类
    └── config/
    ├── WebConfig.java
    └── SwaggerConfig.java

    父 POM 示例:

    <?xml version=\”1.0\” encoding=\”UTF-8\”?>
    <project xmlns=\”http://maven.apache.org/POM/4.0.0\”
    xmlns:xsi=\”http://www.w3.org/2001/XMLSchema-instance\”
    xsi:schemaLocation=\”http://maven.apache.org/POM/4.0.0
    http://maven.apache.org/xsd/maven-4.0.0.xsd\”
    >

    <modelVersion>4.0.0</modelVersion>

    <groupId>com.example</groupId>
    <artifactId>ecommerce-platform</artifactId>
    <version>1.0.0-SNAPSHOT</version>
    <packaging>pom</packaging>

    <modules>
    <module>ecommerce-common</module>
    <module>ecommerce-order</module>
    <module>ecommerce-product</module>
    <module>ecommerce-inventory</module>
    <module>ecommerce-payment</module>
    <module>ecommerce-user</module>
    <module>ecommerce-web</module>
    </modules>

    <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>3.2.0</version>
    </parent>

    <properties>
    <java.version>17</java.version>
    <ddd.version>1.0.0</ddd.version>
    </properties>

    <dependencyManagement>
    <dependencies>
    <!– 公共模块 –>
    <dependency>
    <groupId>com.example</groupId>
    <artifactId>ecommerce-common</artifactId>
    <version>${project.version}</version>
    </dependency>

    <!– 订单模块 –>
    <dependency>
    <groupId>com.example</groupId>
    <artifactId>ecommerce-order</artifactId>
    <version>${project.version}</version>
    </dependency>
    </dependencies>
    </dependencyManagement>
    </project>

    模块 POM 示例(订单模块):

    <?xml version=\”1.0\” encoding=\”UTF-8\”?>
    <project xmlns=\”http://maven.apache.org/POM/4.0.0\”
    xmlns:xsi=\”http://www.w3.org/2001/XMLSchema-instance\”
    xsi:schemaLocation=\”http://maven.apache.org/POM/4.0.0
    http://maven.apache.org/xsd/maven-4.0.0.xsd\”
    >

    <modelVersion>4.0.0</modelVersion>

    <parent>
    <groupId>com.example</groupId>
    <artifactId>ecommerce-platform</artifactId>
    <version>1.0.0-SNAPSHOT</version>
    </parent>

    <artifactId>ecommerce-order</artifactId>
    <packaging>jar</packaging>

    <dependencies>
    <!– 公共模块 –>
    <dependency>
    <groupId>com.example</groupId>
    <artifactId>ecommerce-common</artifactId>
    </dependency>

    <!– Spring Boot –>
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>

    <!– 测试 –>
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
    </dependency>
    </dependencies>
    </project>


    21.4 各层详细设计

    21.4.1 接口层(Interfaces Layer)

    【原理】

    接口层是系统与外部世界的交互边界,职责包括:

    • 接收 HTTP 请求
    • 参数验证
    • 调用应用服务
    • 响应封装

    【代码示例】

    // ========== 接口层目录结构 ==========

    interfaces/
    ├── controller/
    │ ├── OrderController.java # 订单控制器
    │ └── OrderQueryController.java # 订单查询控制器
    ├── dto/
    │ ├── request/
    │ │ ├── CreateOrderRequest.java
    │ │ ├── PayOrderRequest.java
    │ │ └── CancelOrderRequest.java
    │ └── response/
    │ ├── OrderResponse.java
    │ └── OrderListResponse.java
    ├── assembler/
    │ └── OrderAssembler.java # DTO 转换器
    └── facade/
    └── OrderServiceFacade.java # 外观模式(可选)

    // ========== OrderController.java ==========
    package com.example.order.interfaces.controller;

    @RestController
    @RequestMapping(\”/api/v1/orders\”)
    @Validated
    @Tag(name = \”订单管理\”, description = \”订单相关接口\”)
    public class OrderController {

    private final OrderApplicationService orderService;

    @PostMapping
    @Operation(summary = \”创建订单\”)
    public ApiResponse<CreateOrderResponse> createOrder(
    @Valid @RequestBody CreateOrderRequest request) {


    CreateOrderCommand command = OrderAssembler.toCommand(request);
    OrderId orderId = orderService.createOrder(command);
    return ApiResponse.success(new CreateOrderResponse(orderId));
    }

    @PostMapping(\”/{orderId}/payment\”)
    @Operation(summary = \”支付订单\”)
    public ApiResponse<Void> payOrder(
    @PathVariable String orderId,
    @Valid @RequestBody PayOrderRequest request) {


    PayOrderCommand command = new PayOrderCommand(
    OrderId.of(orderId),
    request.getPaymentMethod(),
    request.getPassword()
    );
    orderService.payOrder(command);
    return ApiResponse.success();
    }

    @PostMapping(\”/{orderId}/cancellation\”)
    @Operation(summary = \”取消订单\”)
    public ApiResponse

    赞(0)
    未经允许不得转载:171主机测评 » DDD-021:Spring Boot + DDD 项目结构
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址