You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

六边形架构中:领域实体与端口的领域函数划分规则咨询

领域实体与Ports间领域专属函数的划分规则

核心原则

领域实体是领域逻辑的核心载体,负责封装自身的属性和固有行为;Ports是领域层与外部(应用层、基础设施层)交互的边界接口,处理需要跨边界协调或依赖外部资源的逻辑。两者的划分可以从以下几个维度判断:

1. 函数是否属于实体的"固有行为"

如果函数的逻辑完全依赖实体自身的属性,是实体作为领域对象"天生该会"的能力,就应该放在实体里:

  • 比如calculateBalance():银行账户本身就需要知道如何根据自身的起始余额、交易记录计算当前余额,这是它的固有行为,属于实体范畴。
  • 再比如账户的lock()/unlock():控制自身状态的操作,也属于实体的固有行为。

反之,如果逻辑需要协调多个实体、或者依赖外部上下文,就该放在Ports中。

2. 区分创建逻辑的不同场景

createAccount这类创建函数的归属分两种情况:

  • 纯初始化逻辑:如果只是根据account id、starting balance等参数创建实体实例,同时做基础的领域校验(比如起始余额不能为负数),这部分属于实体的构造函数或静态方法,放在实体里。
  • 带外部交互的创建逻辑:如果创建账户需要联动外部资源(比如生成全局唯一ID、保存到数据库、发送创建通知),这部分逻辑要放在Ports的应用服务或领域服务接口中——由Ports负责协调实体初始化和外部交互的流程。

3. 是否依赖外部资源/服务

判断函数是否需要调用外部依赖:

  • 若函数只依赖实体自身数据,无外部依赖(比如calculateBalance只用到自身的交易列表和起始余额),放在实体里。
  • 若函数需要依赖外部服务(比如计算外币账户余额需要获取实时汇率),则把依赖外部的逻辑抽离到Ports的被驱动端口(比如汇率服务端口),实体只提供基础计算逻辑,由Ports传入外部获取的数据。

4. 结合六边形架构的Port分类

六边形架构的Ports分两类,对应不同的逻辑边界:

  • 驱动端口(Driving Ports):作为外部触发领域逻辑的入口(比如应用服务接口),会调用实体的方法完成核心逻辑,再处理外部响应。
  • 被驱动端口(Driven Ports):定义领域层依赖的外部服务接口(比如数据库存储、第三方API),实体不会直接依赖这些端口,而是由Ports层的实现来协调。

实体的行为始终是纯领域逻辑,不依赖任何Port接口;Ports则作为"桥梁",连接实体与外部世界。

举个实际对比例子

  • 实体中的方法:

    public class BankAccount {
        private String accountId;
        private BigDecimal startingBalance;
        private List<Transaction> transactions;
    
        // 纯初始化构造,带领域校验
        public BankAccount(String accountId, BigDecimal startingBalance) {
            if (startingBalance.compareTo(BigDecimal.ZERO) < 0) {
                throw new IllegalArgumentException("起始余额不能为负");
            }
            this.accountId = accountId;
            this.startingBalance = startingBalance;
            this.transactions = new ArrayList<>();
        }
    
        // 固有行为:计算余额
        public BigDecimal calculateBalance() {
            return transactions.stream()
                .map(Transaction::getAmount)
                .reduce(startingBalance, BigDecimal::add);
        }
    }
    
  • Ports中的应用服务:

    // 驱动端口接口
    public interface AccountServicePort {
        BankAccount createAccount(String accountId, BigDecimal startingBalance);
    }
    
    // 驱动端口实现
    public class AccountServiceImpl implements AccountServicePort {
        private final AccountRepositoryPort accountRepository; // 被驱动端口
    
        @Override
        public BankAccount createAccount(String accountId, BigDecimal startingBalance) {
            BankAccount account = new BankAccount(accountId, startingBalance);
            accountRepository.save(account); // 调用外部存储
            // 发送账户创建事件等外部交互逻辑
            return account;
        }
    }
    

内容的提问来源于stack exchange,提问作者Vinn

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.22 21:30:19