Skip to content
java
tour-mate-platform/
├── pom.xml (或 build.gradle.kts)
├── README.md
├── settings.gradle.kts
└── src/
    └── main/
        └── kotlin (或 java)/
            └── com.tourmate/
                ├── TourMateApplication.kt                 # SpringBoot 启动类

                ├── common/                                 # 全项目公用(通用子域)
                │   ├── exception/
                │   ├── result/
                │   └── util/

                ├── adapter/                                # 所有防腐层实现(基础设施层)
                │   ├── web/                                # 触发层:Controller、DTO、VO
                │   │   ├── activity/                       # 出行活动相关接口
                │   │   │   ├── dto/
                │   │   │   └── ActivityController.kt
                │   │   └── user/
                │   │
                │   └── persistence/                        # 数据库防腐层
                │       └── activity/
                │           ├── jpa/
                │           │   ├── TravelActivityJpaRepository.kt
                │           │   └── entity/                  # JPA Entity(脏数据)
                │           └── TravelActivityRepositoryImpl.kt   # 真正的防腐层实现

                ├── application/                            # 应用层(输入端口)
                │   └── activity/
                │       ├── port/in/                        # 输入端口(UseCase)
                │       │   ├── CreateActivityUseCase.kt
                │       │   ├── JoinActivityUseCase.kt
                │       │   └── CancelActivityUseCase.kt
                │       └── service/                        # 应用服务(编排)
                │           └── ActivityApplicationService.kt

                ├── domain/                                 # 核心领域层(最干净!)
                │   ├── activity/                           # 出行活动上下文(核心域)
                │   │   ├── model/                      # 聚合根 + 值对象 + 领域事件
                │   │   │   ├── TravelActivity.kt       # 终极聚合根
                │   │   │   ├── ActivityEnrollment.kt
                │   │   │   ├── valueobject/            # Capacity、ActivityTime、Creator 等
                │   │   │   └── event/                  # 领域事件
                │   │   │       ├── ActivityCreated.kt
                │   │   │       ├── ActivityFullyBooked.kt
                │   │   │       └── ActivityCancelled.kt
                │   │   ├── port/out/                   # 输出端口
                │   │   │   ├── TravelActivityRepository.kt
                │   │   │   └── NotificationPort.kt
                │   │   └── service/                    # 领域服务(极少用)
                │   │
                │   ├── user/                               # 用户身份与信用上下文(支撑域)
                │   └── content/                            # 内容分享上下文(通用域,初期可空)

                └── infrastructure/                         # 基础设施(事件监听、外部适配)
                    ├── event/                              # 领域事件监听
                    │   └── activity/
                    │       └── ActivityEventListener.kt    # 监听满员、取消等事件 → 发通知
                    └── messaging/                          # 通知、聊天适配器
                        └── NotificationAdapter.kt

场景:用户点【加入活动】按钮(还剩 1 个名额,100 人同时点)

完整调用链(真实代码顺序 + 每一层在干什么)

步骤代码位置(包名)具体类/方法这一层到底在干嘛?(大白话)如果没有这一层会死在哪?
1adapter/web/activityActivityController.join(@PathVariable id)只负责“收快递”:把 HTTP 请求转成命令,几乎不写业务逻辑没有也行,但容易把 HTTP 协议污染业务
2application/activity/port/inJoinActivityUseCase.join(activityId, userId)门口保安:只负责“接需求、开事务、编排流程”,一句话调用领域没有这一层,Controller 直接调聚合根,容易写成大杂烩
3domain/activity/modelTravelActivity.join(userId)★ 灵魂所在!活的活动自己说:“我看看我现在能不能让你进来” → 检查状态、人数 → 改自己状态没有这一层,所有规则散在 Service,超卖、状态错、权限漏
4domain/activity/port/outTravelActivityRepository.load(id)纯净接口:领域只说“我要整个活动”,不管你是 MySQL 还是火星文没有这一层,聚合根里直接写 SQL,领域被数据库污染
5adapter/persistence/activityTravelActivityRepositoryImpl.load()真正的脏活累活:从数据库拼装出一个“活的”TravelActivity(翻译官)没有这一层,你永远摆脱不了“表驱动开发”
6domain/activity/modelTravelActivity.join() 内部聚合根发现已满员 → 自己改状态 → 自己抛异常 或 自己发布领域事件这一步是防超卖、发通知的根本保证
7adapter/persistence/activityTravelActivityRepositoryImpl.save(activity)再把“活的”活动拆成多条 SQL 存回去(翻译官反向工作)同上
8infrastructure/event/activityActivityEventListener.on(ActivityFullyBooked)监听器收到“活动满员了”事件 → 发推送、发站内信、更新推荐缓存没有这一层,通知逻辑散在各处,容易忘

真实代码执行顺序(8 步完整版)

text

1. 用户点按钮 → 
2. ActivityController.join() → 
3. JoinActivityUseCase.join() → 
4. TravelActivityRepository.load() → 
5. TravelActivityRepositoryImpl.load() → 执行 2 条 SQL 拼成 TravelActivity 对象 → 
6. TravelActivity.join(userId) → 纯内存操作,判断、改状态、发布事件 → 
7. TravelActivityRepositoryImpl.save() → 把修改后的聚合根拆成 update + insert → 
8. 事务提交 → 领域事件发布 → ActivityEventListener 发通知

每一层“值不值”的终极回答

层级你现在觉得“多此一举”3 年后你会跪着感谢它的理由
Controller多写几行转换HTTP 协议改了(REST → GraphQL)只改这一层
UseCase好像就是个传话筒以后微信小程序、H5、后台管理都要调同一个业务,只改这一层就行
聚合根写着好麻烦所有并发、状态、权限、不变量只在这一个地方,改一次全站生效,永不出 bug
Repository Port多写个接口干嘛明天换 MySQL → ClickHouse、换事件溯源、换 Redis 缓存,只改 Impl 就行
Repository Impl最啰嗦数据库加 10 个字段,你只改这一个文件,其他 300 个文件不动
领域事件 + 监听器又是一堆类发通知、更新推荐、写审计日志、同步搜索索引……所有副作用只写一次,永不遗漏
步骤你的理解(已经非常对)我再加 5 分的精准修正(你以后可以直接背)
1Controller 触发加入活动完全正确!这一层叫 Driving Adapter(驱动适配器),只负责把外部世界(HTTP)翻译成领域能听懂的命令
2到了 application 的 UseCase 进行服务编排完全正确!这一层叫 Application Layer(应用层),正式名称是 Input Port + Application Service,只做“事务脚本编排”,不放业务规则
3调用核心领域层的聚合根+值对象+领域事件完全正确!这一层叫 Domain Layer(领域层),是真正的王,这里放“活的对象”和业务不变量
4领域层需要持久化 → 调用输出端口(Repository Port)完全正确!这叫 Output Port(输出端口),领域只定义“我要保存/加载”,不管怎么实现
5输出端口的具体实现(RepositoryImpl)→ 操作数据库完全正确!这叫 Driven Adapter(被驱动适配器)/防腐层实现,所有脏活(SQL、JPA、MyBatis)全在这
6(你漏了最重要的一环)领域事件发布 → 事件监听器发通知等补 5 分! 聚合根在 join() 里 raise ActivityFullyBooked 事件 → 事件总线 → 监听器(在 infrastructure 层)发推送、更新推荐缓存,这才叫真正的“松耦合”

你现在可以背下来的标准 6 层调用链(大厂面试必问)

text

1. Driving Adapter(Controller)
   ↓(调用)
2. Input Port(UseCase 接口)
   ↓(实现)
3. Application Service(事务 + 编排)
   ↓(调用)
4. Domain Model(聚合根执行核心业务 + raise 领域事件)
   ↓(需要持久化时调用)
5. Output Port(Repository 接口)
   ↓(实现)
6. Driven Adapter(RepositoryImpl + 事件监听器)

你现在可以秒杀 99% 伪 DDD 程序员的 3 个面试题

  1. “领域事件放哪一层?” → raise 在聚合根里(Domain),监听器在 infrastructure(Driven Adapter)
  2. “为什么 Controller 不能直接调 Repository?” → 因为那样就绕过了聚合根,业务规则就散了,依赖倒置也完蛋
  3. “Application Service 和 Domain Service 区别?” → Application Service 编排(事务、调用多个聚合),Domain Service 放“跨聚合的纯领域行为”(比如转账)

Powered by VitePress